pax_global_header00006660000000000000000000000064132266742510014522gustar00rootroot0000000000000052 comment=23b253e9eb3296a03afd5d52908726da1231f0dd cppcheck-1.82/000077500000000000000000000000001322667425100132345ustar00rootroot00000000000000cppcheck-1.82/.gitignore000066400000000000000000000022701322667425100152250ustar00rootroot00000000000000*.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 # VS generated files *.aps *.idb *.ncb *.obj *.opensdf *.orig *.pdb *.sdf *.suo *.user # 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 cppcheck-1.82/.mailmap000066400000000000000000000054471322667425100146670ustar00rootroot00000000000000Andreas 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.82/.travis.yml000066400000000000000000000147731322667425100153610ustar00rootroot00000000000000language: cpp dist: trusty sudo: required compiler: - gcc - clang env: global: # unfortunately we need this to stay within 50min timelimit given by travis. # this also turns off the debug/warning cxxflags - ORIGINAL_CXXFLAGS="-include lib/cxx11emu.h -pedantic -Wall -Wextra -Wabi -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wshadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar -D_GLIBCXX_DEBUG -g " - CXXFLAGS="${ORIGINAL_CXXFLAGS} -O2 -march=native -Wstrict-aliasing=2 -Werror=strict-aliasing" matrix: # special CXXFLAGS for maximum speed, overrides global CXXFLAGS, CHECK_CLANG is the var that controls if we download and check clang in that travis job - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" MAKEFLAGS="HAVE_RULES=yes" SRCDIR=build VERIFY=1 - SRCDIR=build CHECK_CLANG=yes VERIFY=1 - SRCDIR=build CHECK_LLVM=yes VERIFY=1 - CHECK_MAKEFILE_REGEN=true matrix: # do notify immediately about it when a job of a build fails. fast_finish: true # exclude clang checking with clang binary, not needed exclude: - compiler: gcc env: SRCDIR=build CHECK_CLANG=yes VERIFY=1 - compiler: gcc env: SRCDIR=build CHECK_LLVM=yes VERIFY=1 - compiler: gcc env: CHECK_MAKEFILE_REGEN=true allow_failures: - compiler: clang env: CHECK_MAKEFILE_REGEN=true before_install: # install needed deps - sudo apt-get update -qq - sudo apt-get install -qq python-pygments qt5-default qt5-qmake qtbase5-dev qtcreator libxml2-utils libpcre3 gdb unzip script: # fail the entire job as soon as one of the subcommands exits non-zero to save time and resources - set -e # download clang git, compile cppcheck, run cppcheck on clang code to look for crashes in cppcheck. if this is done, terminate build - if [[ "$CHECK_CLANG" == "yes" ]] && [[ "$CC" == "clang" ]]; then wget "https://github.com/llvm-mirror/clang/archive/bcaf7f2abe47b0dab055f1a0ec011ed9c2a3d3ea.zip" & make -j 4 & wait; unzip bcaf7f2abe47b0dab055f1a0ec011ed9c2a3d3ea.zip > /dev/null; touch /tmp/clang.cppcheck; cd ./clang-bcaf7f2abe47b0dab055f1a0ec011ed9c2a3d3ea ; ../cppcheck . --max-configs=1 --enable=all --inconclusive --exception-handling --template="{callstack} ({severity}) {message} [{id}]" -iINPUTS -itest/Driver/Inputs/gen-response.c -itest/Index/index-many-logical-ops.c -itest/Sema/many-logical-ops.c -j 2 |& tee /tmp/clang.cppcheck; cd ../ ; echo "CLANG" ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/clang.cppcheck; exit; fi # check llvm as well - if [[ "$CHECK_LLVM" == "yes" ]] && [[ "$CC" == "clang" ]]; then wget "https://github.com/llvm-mirror/llvm/archive/7733e74e5454d6f18da28c06be917c1e73d12d01.zip" & make -j 4 & wait; unzip 7733e74e5454d6f18da28c06be917c1e73d12d01.zip > /dev/null; touch /tmp/llvm.cppcheck; cd ./llvm-7733e74e5454d6f18da28c06be917c1e73d12d01 ; ../cppcheck . --max-configs=1 --enable=all --inconclusive --exception-handling --template="{callstack} ({severity}) {message} [{id}]" -j 2 |& tee /tmp/llvm.cppcheck; cd ../ ; echo "LLVM" ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/llvm.cppcheck; exit; fi # check if dmake needs to be rerun but if yes, don't fail the build but notify us. # to update dmake: "make dmake; ./dmake; and commit - echo "If the following commmand fails, run 'make dmake; make run-dmake' and commit the resulting change." - if [[ "$CHECK_MAKEFILE_REGEN" == "true" ]]; then make -s dmake; make -s run-dmake ; git diff --exit-code; exit; fi # check with TEST_MATHLIB_VALUE enabled - touch lib/mathlib.cpp test/testmathlib.cpp - echo $CXXFLAGS - make -s test -j4 CPPFLAGS=-DTEST_MATHLIB_VALUE - touch lib/mathlib.cpp test/testmathlib.cpp # compile cppcheck, default build - echo $CXXFLAGS - make -s test -j4 # compile gui - cd gui - qmake - echo $CXXFLAGS - make -s -j4 # building gui generates some more files that cppcheck can check, so check the repo *after* building gui - cd ../ # use same hack as for clang to work around cppchecks broken exit status with -j 2 ; create file, tee everything to the file and stdout, grep for errors in the file - touch /tmp/cppcheck.cppcheck - ./cppcheck --error-exitcode=1 --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml/ -Icli --enable=style,performance,portability,warning,internal --exception-handling --suppressions-list=.travis_suppressions -itest/synthetic -iaddons -igui . -j 2 |& tee /tmp/cppcheck.cppcheck # check gui with qt settings - ./cppcheck --library=qt --error-exitcode=1 -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml/ -Icli --enable=style,performance,portability,warning,internal --exception-handling -j 2 gui -igui/test |& tee --append /tmp/cppcheck.cppcheck - sh -c "! grep '^\[' /tmp/cppcheck.cppcheck" # run extra tests - tools/generate_and_run_more_tests.sh # check test/cfg - make checkcfg - make validateCFG - 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 -j4 - cd ../ # note: trusty on travis has python pygments disabled so disable these tests on travis ## 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 install # rm everything - git clean -dfx # check what happens if we want to install it to some other dir, - echo $CXXFLAGS - make -s SRCDIR=build CFGDIR=/usr/share/cppcheck/cfg -j 4 - sudo make SRCDIR=build CFGDIR=/usr/share/cppcheck/cfg install - sudo mkdir -p /usr/share/cppcheck/cfg - sudo install -D ./cfg/* -t /usr/share/cppcheck/cfg # check if it actually works: - /usr/bin/cppcheck ./cli # check if reduce tool compiles - echo $CXXFLAGS - make -s reduce -j 4 # check if showtime=top5 works - ./tools/test_showtimetop5.sh # check the files in cgf dir with xmllint - xmllint --noout cfg/* # check matchcompiler - ./tools/test_matchcompiler.py # check --dump - ./cppcheck test/testpreprocessor.cpp --dump - xmllint --noout test/testpreprocessor.cpp.dump # check addons/misra.py # - ./cppcheck --dump addons/misra-test.c # - python addons/misra.py -verify addons/misra-test.c.dump notifications: irc: channels: - "irc.freenode.org#cppcheck" template: - "[%{commit} : %{author}] %{message}" - "%{build_url}" skip_join: true cppcheck-1.82/.travis_suppressions000066400000000000000000000007561322667425100174120ustar00rootroot00000000000000unusedPrivateFunction:test/testcmdlineparser.cpp redundantNextPrevious:test/testtoken.cpp simplePatternError:test/testtoken.cpp uselessAssignmentPtrArg:build/checkstl.cpp uselessAssignmentPtrArg:build/tokenize.cpp passedByValue:lib/symboldatabase.h passedByValue:lib/library.h knownConditionTrueFalse:lib/platform.cpp knownConditionTrueFalse:build/* nullPointer:lib/checkother.cpp nullPointer:build/checkother.cpp *:gui/test* *:test/test.cxx *:test/cfg* *:externals* *:htmlreport* *:samples* cppcheck-1.82/AUTHORS000066400000000000000000000065131322667425100143110ustar00rootroot00000000000000The cppcheck team, in alphabetical order: Abhishek Bharadwaj Abigail Buccaneer Ahti Legonkov Akhilesh Nema Akio Idehara Albert Aribaud Aleksandr Pikalev Aleksey Palazhchenko Alexander Alekseev Alexander Mai Alexey Eryomenko Alexey Zhikhartsev Ali Can Demiralp Ameen Ali Andreas Bießmann Andrei Karas Andreas Pokorny Andrew C. Martin Andy Maloney Aneesh Azhakesan S Ankita Gupta Antti Tuppurainen Anurag Garg Armin Müller Arpit Chaudhary Ayaz Salikhov August Sodora Baris Demiray Bartlomiej Grzeskowiak Ben T Benjamin Bannier Benjamin Fovet Benjamin Goose Benjamin Kramer Benjamin Wolsey Bill Egert Björge Dijkstra booga Boris Barbulovski Boris Egorov Carlo Marcelo Arenas Belon Cary R Changkyoon Kim Christian Ehrlicher Chuck Larson Daniel Marjamäki David Korth Debrard Sebastien Deepak Gupta dencat Diego de las Heras Dirk Jagdmann Dmitry Marakasov Dmitry-Me Dmitriy Duraffort Edoardo Prezioso Elbert Pol Emmanuel Blot Eric Sesterhenn Erik Hovland Erik Lax Ettl Martin Evgeny Mandrikov Felipe Pena Felix Geyer Florin Iucha Frank Zingsheim fu7mu4 Galimov Albert Garrett Bodily gaurav kaushik Gennady Feldman Gerhard Zlabinger Gianfranco Costamagna Gianluca Scacco Gleydson Soares Goran Džaferi Graham Whitted Greg Hewgill Guillaume Miossec Gustav Palmqvist Günther Makulik Harald Scheidl Heinrich Schuchardt Heiko Eißfeldt Henrik Nilsson He Yuqi Hoang Tuan Su Ivan Maidanski Ivan Ryabov Iván Matellanes Jakub Melka Jan Hellwig Jay Sigbrandt Jean-Pierre Luxon Jens Bäckman János Maros Jérémy Lefaure Johan Samuelson John Smits John-Paul Ore Jonathan Neuschäfer Jonathan Thackray Jose Roquette Joshua Beck Joshua Rogers Julian Santander Jussi Lehtola Kamil Dudka Kartik Bajaj Kevin Christian Kimmo Varis Konrad Windszus Kumar Ashwani larudwer Lauri Nurmi Lau bakman Leandro Lisboa Penz Lena Herscheid Lieven de Cock lioncash Lucas Manuel Rodriguez Luis Díaz Más Luxon Jean-Pierre Malcolm Parsons Marc-Antoine Perennou Marcel Raad Marek Zmysłowski Marian Klymov Mark de Wever Markus Elfring Martin Ettl Martin Exner Martin Güthle Massimo Paladin Mateusz Pusz Mathias De Maré Matthias Krüger Matthias Schmieder Matt Johnson Mavik Miika-Petteri Matikainen Mika Attila Mike Tzou Milhan Kim Mohit Mate Monika Lukow Moritz Barsnick Moritz Lipp Moshe Kaplan ms Neszt Tibor Nguyen Duong Tuan Ni2c2k Nicolás Alvarez Nicolas Le Cam Nilesh Kumar Ogawa KenIchi Oleksandr Redko Oliver Stoeneberg Pavel Bibergal Pavel Roschin Pavol Misik Pete Johns Peter Pentchev Philipp Kloke Pierre Schweitzer Pino Toscano Pranav Khanna Ramzan Bekbulatov Raphael Geissert Reijo Tomperi Richard Quirk Rick van der Sluijs rivdsl Robert Habrich Robert Morin Roberto Martelloni Robert Reif rofl0r Roman Zaytsev Borisovich root Rosen Penev Ryan Pavlik Rudolf Grauberger Samir Aguiar Sam Truscott Samuel Degrande Sandeep Dutta Sebastian Matuschka Sébastien Debrard Simon Kagstrom Simon Martin Simon Shanks Slava Semushin Stas Cymbalov Stefan Beller Stefan Naewe Stefan Weil Steve Browne Steve Duan Steven Myint Teddy Didé Thomas Arnhold Thomas Jarosch Thomas Otto Thomas Sondergaard Tim Gerundt Tobias Weibel Tom Pollok Toralf Förster Troshin V.S. Valerii Lashmanov Vasily Maslyukov Veli-Matti Visuri WenChung Chiu Vesa Pikki Ville Skyttä x29a XhmikosR Xuecheng Zhang Zachary Blair Zhao Qifa Zhiyuan Zhang Дмитрий Старцев GUI graphics courtesy of Tango Desktop Project: http://tango.freedesktop.org cppcheck-1.82/CMakeLists.txt000066400000000000000000000013001322667425100157660ustar00rootroot00000000000000project(CppCheck) cmake_minimum_required(VERSION 2.8.4) 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) file(GLOB cfgs "cfg/*.cfg") 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 include(cmake/printInfo.cmake REQUIRED) cppcheck-1.82/COPYING000066400000000000000000001045131322667425100142730ustar00rootroot00000000000000 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.82/Cppcheck.xcodeproj/000077500000000000000000000000001322667425100167505ustar00rootroot00000000000000cppcheck-1.82/Cppcheck.xcodeproj/project.pbxproj000066400000000000000000002331311322667425100220270ustar00rootroot00000000000000// !$*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.82/Makefile000066400000000000000000001567421322667425100147130ustar00rootroot00000000000000# 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 # folder where lib/*.cpp files are located ifndef SRCDIR SRCDIR=lib endif ifeq ($(SRCDIR),build) ifdef VERIFY matchcompiler_S := $(shell python tools/matchcompiler.py --verify) else matchcompiler_S := $(shell python tools/matchcompiler.py) endif endif ifdef CFGDIR CFG=-DCFGDIR=\"$(CFGDIR)\" else CFG= 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 -include lib/cxx11emu.h -DNDEBUG -Wall -Wno-sign-compare endif 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 -Iexternals/simplecpp -Iexternals/tinyxml endif ifndef INCLUDE_FOR_CLI INCLUDE_FOR_CLI=-Ilib -Iexternals/simplecpp -Iexternals/tinyxml endif ifndef INCLUDE_FOR_TEST INCLUDE_FOR_TEST=-Ilib -Icli -Iexternals/simplecpp -Iexternals/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 = $(SRCDIR)/analyzerinfo.o \ $(SRCDIR)/astutils.o \ $(SRCDIR)/check.o \ $(SRCDIR)/check64bit.o \ $(SRCDIR)/checkassert.o \ $(SRCDIR)/checkautovariables.o \ $(SRCDIR)/checkbool.o \ $(SRCDIR)/checkboost.o \ $(SRCDIR)/checkbufferoverrun.o \ $(SRCDIR)/checkclass.o \ $(SRCDIR)/checkcondition.o \ $(SRCDIR)/checkexceptionsafety.o \ $(SRCDIR)/checkfunctions.o \ $(SRCDIR)/checkinternal.o \ $(SRCDIR)/checkio.o \ $(SRCDIR)/checkleakautovar.o \ $(SRCDIR)/checkmemoryleak.o \ $(SRCDIR)/checknullpointer.o \ $(SRCDIR)/checkother.o \ $(SRCDIR)/checkpostfixoperator.o \ $(SRCDIR)/checksizeof.o \ $(SRCDIR)/checkstl.o \ $(SRCDIR)/checkstring.o \ $(SRCDIR)/checktype.o \ $(SRCDIR)/checkuninitvar.o \ $(SRCDIR)/checkunusedfunctions.o \ $(SRCDIR)/checkunusedvar.o \ $(SRCDIR)/checkvaarg.o \ $(SRCDIR)/cppcheck.o \ $(SRCDIR)/errorlogger.o \ $(SRCDIR)/importproject.o \ $(SRCDIR)/library.o \ $(SRCDIR)/mathlib.o \ $(SRCDIR)/path.o \ $(SRCDIR)/pathmatch.o \ $(SRCDIR)/platform.o \ $(SRCDIR)/preprocessor.o \ $(SRCDIR)/settings.o \ $(SRCDIR)/suppressions.o \ $(SRCDIR)/symboldatabase.o \ $(SRCDIR)/templatesimplifier.o \ $(SRCDIR)/timer.o \ $(SRCDIR)/token.o \ $(SRCDIR)/tokenize.o \ $(SRCDIR)/tokenlist.o \ $(SRCDIR)/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/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/testpostfixoperator.o \ test/testpreprocessor.o \ test/testrunner.o \ test/testsamples.o \ test/testsimplifytemplate.o \ test/testsimplifytokens.o \ test/testsimplifytypedef.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 $(SRCDIR)/pathmatch.o $(SRCDIR)/path.o externals/simplecpp/simplecpp.o $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) run-dmake: dmake ./dmake reduce: tools/reduce.o $(LIBOBJ) $(EXTOBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) clean: rm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner reduce dmake cppcheck cppcheck.1 man: man/cppcheck.1 man/cppcheck.1: $(MAN_SOURCE) $(XP) $(DB2MAN) $(MAN_SOURCE) tags: ctags -R --exclude=doxyoutput --exclude=test/cfg cli externals gui lib test install: cppcheck install -d ${BIN} install cppcheck ${BIN} install addons/*.py ${BIN} install addons/*/*.py ${BIN} install htmlreport/cppcheck-htmlreport ${BIN} ifdef CFGDIR install -d ${DESTDIR}${CFGDIR} install -m 644 cfg/* ${DESTDIR}${CFGDIR} 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} ###### Build $(SRCDIR)/analyzerinfo.o: lib/analyzerinfo.cpp lib/cxx11emu.h lib/analyzerinfo.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/path.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/analyzerinfo.o $(SRCDIR)/analyzerinfo.cpp $(SRCDIR)/astutils.o: lib/astutils.cpp lib/cxx11emu.h lib/astutils.h lib/library.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/standards.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/timer.h lib/symboldatabase.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/astutils.o $(SRCDIR)/astutils.cpp $(SRCDIR)/check.o: lib/check.cpp lib/cxx11emu.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/check.o $(SRCDIR)/check.cpp $(SRCDIR)/check64bit.o: lib/check64bit.cpp lib/cxx11emu.h lib/check64bit.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/check64bit.o $(SRCDIR)/check64bit.cpp $(SRCDIR)/checkassert.o: lib/checkassert.cpp lib/cxx11emu.h lib/checkassert.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkassert.o $(SRCDIR)/checkassert.cpp $(SRCDIR)/checkautovariables.o: lib/checkautovariables.cpp lib/cxx11emu.h lib/checkautovariables.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkautovariables.o $(SRCDIR)/checkautovariables.cpp $(SRCDIR)/checkbool.o: lib/checkbool.cpp lib/cxx11emu.h lib/checkbool.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkbool.o $(SRCDIR)/checkbool.cpp $(SRCDIR)/checkboost.o: lib/checkboost.cpp lib/cxx11emu.h lib/checkboost.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkboost.o $(SRCDIR)/checkboost.cpp $(SRCDIR)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp lib/cxx11emu.h lib/checkbufferoverrun.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkbufferoverrun.o $(SRCDIR)/checkbufferoverrun.cpp $(SRCDIR)/checkclass.o: lib/checkclass.cpp lib/cxx11emu.h lib/checkclass.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkclass.o $(SRCDIR)/checkclass.cpp $(SRCDIR)/checkcondition.o: lib/checkcondition.cpp lib/cxx11emu.h lib/checkcondition.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkcondition.o $(SRCDIR)/checkcondition.cpp $(SRCDIR)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/cxx11emu.h lib/checkexceptionsafety.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkexceptionsafety.o $(SRCDIR)/checkexceptionsafety.cpp $(SRCDIR)/checkfunctions.o: lib/checkfunctions.cpp lib/cxx11emu.h lib/checkfunctions.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkfunctions.o $(SRCDIR)/checkfunctions.cpp $(SRCDIR)/checkinternal.o: lib/checkinternal.cpp lib/cxx11emu.h lib/checkinternal.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkinternal.o $(SRCDIR)/checkinternal.cpp $(SRCDIR)/checkio.o: lib/checkio.cpp lib/cxx11emu.h lib/checkio.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkio.o $(SRCDIR)/checkio.cpp $(SRCDIR)/checkleakautovar.o: lib/checkleakautovar.cpp lib/cxx11emu.h lib/checkleakautovar.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/checkmemoryleak.h lib/checknullpointer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkleakautovar.o $(SRCDIR)/checkleakautovar.cpp $(SRCDIR)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/cxx11emu.h lib/checkmemoryleak.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkmemoryleak.o $(SRCDIR)/checkmemoryleak.cpp $(SRCDIR)/checknullpointer.o: lib/checknullpointer.cpp lib/cxx11emu.h lib/checknullpointer.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checknullpointer.o $(SRCDIR)/checknullpointer.cpp $(SRCDIR)/checkother.o: lib/checkother.cpp lib/cxx11emu.h lib/checkother.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkother.o $(SRCDIR)/checkother.cpp $(SRCDIR)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/cxx11emu.h lib/checkpostfixoperator.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkpostfixoperator.o $(SRCDIR)/checkpostfixoperator.cpp $(SRCDIR)/checksizeof.o: lib/checksizeof.cpp lib/cxx11emu.h lib/checksizeof.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checksizeof.o $(SRCDIR)/checksizeof.cpp $(SRCDIR)/checkstl.o: lib/checkstl.cpp lib/cxx11emu.h lib/checkstl.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/checknullpointer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkstl.o $(SRCDIR)/checkstl.cpp $(SRCDIR)/checkstring.o: lib/checkstring.cpp lib/cxx11emu.h lib/checkstring.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkstring.o $(SRCDIR)/checkstring.cpp $(SRCDIR)/checktype.o: lib/checktype.cpp lib/cxx11emu.h lib/checktype.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checktype.o $(SRCDIR)/checktype.cpp $(SRCDIR)/checkuninitvar.o: lib/checkuninitvar.cpp lib/cxx11emu.h lib/checkuninitvar.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/astutils.h lib/checknullpointer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkuninitvar.o $(SRCDIR)/checkuninitvar.cpp $(SRCDIR)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp lib/cxx11emu.h lib/checkunusedfunctions.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkunusedfunctions.o $(SRCDIR)/checkunusedfunctions.cpp $(SRCDIR)/checkunusedvar.o: lib/checkunusedvar.cpp lib/cxx11emu.h lib/checkunusedvar.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkunusedvar.o $(SRCDIR)/checkunusedvar.cpp $(SRCDIR)/checkvaarg.o: lib/checkvaarg.cpp lib/cxx11emu.h lib/checkvaarg.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/checkvaarg.o $(SRCDIR)/checkvaarg.cpp $(SRCDIR)/cppcheck.o: lib/cppcheck.cpp lib/cxx11emu.h lib/cppcheck.h lib/analyzerinfo.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/checkunusedfunctions.h lib/path.h lib/preprocessor.h lib/version.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/cppcheck.o $(SRCDIR)/cppcheck.cpp $(SRCDIR)/errorlogger.o: lib/errorlogger.cpp lib/cxx11emu.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/cppcheck.h lib/analyzerinfo.h lib/importproject.h lib/platform.h lib/utils.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/path.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/errorlogger.o $(SRCDIR)/errorlogger.cpp $(SRCDIR)/importproject.o: lib/importproject.cpp lib/cxx11emu.h lib/importproject.h lib/config.h lib/platform.h lib/utils.h lib/path.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/importproject.o $(SRCDIR)/importproject.cpp $(SRCDIR)/library.o: lib/library.cpp lib/cxx11emu.h lib/library.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/standards.h lib/astutils.h lib/path.h lib/symboldatabase.h lib/token.h lib/valueflow.h lib/tokenlist.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/library.o $(SRCDIR)/library.cpp $(SRCDIR)/mathlib.o: lib/mathlib.cpp lib/cxx11emu.h lib/mathlib.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/mathlib.o $(SRCDIR)/mathlib.cpp $(SRCDIR)/path.o: lib/path.cpp lib/cxx11emu.h lib/path.h lib/config.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/path.o $(SRCDIR)/path.cpp $(SRCDIR)/pathmatch.o: lib/pathmatch.cpp lib/cxx11emu.h lib/pathmatch.h lib/config.h lib/path.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/pathmatch.o $(SRCDIR)/pathmatch.cpp $(SRCDIR)/platform.o: lib/platform.cpp lib/cxx11emu.h lib/platform.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/platform.o $(SRCDIR)/platform.cpp $(SRCDIR)/preprocessor.o: lib/preprocessor.cpp lib/cxx11emu.h lib/preprocessor.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/library.h lib/mathlib.h lib/standards.h lib/path.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/preprocessor.o $(SRCDIR)/preprocessor.cpp $(SRCDIR)/settings.o: lib/settings.cpp lib/cxx11emu.h lib/settings.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/settings.o $(SRCDIR)/settings.cpp $(SRCDIR)/suppressions.o: lib/suppressions.cpp lib/cxx11emu.h lib/suppressions.h lib/config.h lib/path.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/suppressions.o $(SRCDIR)/suppressions.cpp $(SRCDIR)/symboldatabase.o: lib/symboldatabase.cpp lib/cxx11emu.h lib/symboldatabase.h lib/config.h lib/library.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/standards.h lib/token.h lib/valueflow.h lib/platform.h lib/settings.h lib/importproject.h lib/utils.h lib/timer.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/symboldatabase.o $(SRCDIR)/symboldatabase.cpp $(SRCDIR)/templatesimplifier.o: lib/templatesimplifier.cpp lib/cxx11emu.h lib/templatesimplifier.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/templatesimplifier.o $(SRCDIR)/templatesimplifier.cpp $(SRCDIR)/timer.o: lib/timer.cpp lib/cxx11emu.h lib/timer.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/timer.o $(SRCDIR)/timer.cpp $(SRCDIR)/token.o: lib/token.cpp lib/cxx11emu.h lib/token.h lib/config.h lib/mathlib.h lib/valueflow.h lib/errorlogger.h lib/suppressions.h lib/library.h lib/standards.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/token.o $(SRCDIR)/token.cpp $(SRCDIR)/tokenize.o: lib/tokenize.cpp lib/cxx11emu.h lib/tokenize.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/check.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/path.h lib/symboldatabase.h lib/templatesimplifier.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/tokenize.o $(SRCDIR)/tokenize.cpp $(SRCDIR)/tokenlist.o: lib/tokenlist.cpp lib/cxx11emu.h lib/tokenlist.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/path.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/tokenlist.o $(SRCDIR)/tokenlist.cpp $(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/cxx11emu.h lib/valueflow.h lib/config.h lib/astutils.h lib/errorlogger.h lib/suppressions.h lib/library.h lib/mathlib.h lib/standards.h lib/platform.h lib/settings.h lib/importproject.h lib/utils.h lib/timer.h lib/symboldatabase.h lib/token.h lib/tokenlist.h lib/path.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(SRCDIR)/valueflow.o $(SRCDIR)/valueflow.cpp cli/cmdlineparser.o: cli/cmdlineparser.cpp lib/cxx11emu.h cli/cmdlineparser.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h cli/cppcheckexecutor.h cli/filelister.h lib/path.h cli/threadexecutor.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp lib/cxx11emu.h cli/cppcheckexecutor.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/analyzerinfo.h lib/importproject.h lib/platform.h lib/utils.h cli/cmdlineparser.h lib/cppcheck.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h cli/filelister.h lib/path.h lib/pathmatch.h lib/preprocessor.h cli/threadexecutor.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp cli/filelister.o: cli/filelister.cpp lib/cxx11emu.h cli/filelister.h lib/path.h lib/config.h lib/pathmatch.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/filelister.o cli/filelister.cpp cli/main.o: cli/main.cpp lib/cxx11emu.h cli/cppcheckexecutor.h lib/errorlogger.h lib/config.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/main.o cli/main.cpp cli/threadexecutor.o: cli/threadexecutor.cpp lib/cxx11emu.h cli/threadexecutor.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/cppcheck.h lib/analyzerinfo.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h cli/cppcheckexecutor.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp test/options.o: test/options.cpp lib/cxx11emu.h test/options.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/options.o test/options.cpp test/test64bit.o: test/test64bit.cpp lib/cxx11emu.h lib/check64bit.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/test64bit.o test/test64bit.cpp test/testassert.o: test/testassert.cpp lib/cxx11emu.h lib/checkassert.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testassert.o test/testassert.cpp test/testastutils.o: test/testastutils.cpp lib/cxx11emu.h lib/astutils.h lib/settings.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testastutils.o test/testastutils.cpp test/testautovariables.o: test/testautovariables.cpp lib/cxx11emu.h lib/checkautovariables.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testautovariables.o test/testautovariables.cpp test/testbool.o: test/testbool.cpp lib/cxx11emu.h lib/checkbool.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testbool.o test/testbool.cpp test/testboost.o: test/testboost.cpp lib/cxx11emu.h lib/checkboost.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testboost.o test/testboost.cpp test/testbufferoverrun.o: test/testbufferoverrun.cpp lib/cxx11emu.h lib/checkbufferoverrun.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testbufferoverrun.o test/testbufferoverrun.cpp test/testcharvar.o: test/testcharvar.cpp lib/cxx11emu.h lib/checkother.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcharvar.o test/testcharvar.cpp test/testclass.o: test/testclass.cpp lib/cxx11emu.h lib/checkclass.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testclass.o test/testclass.cpp test/testcmdlineparser.o: test/testcmdlineparser.cpp lib/cxx11emu.h lib/platform.h lib/config.h test/redirect.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcmdlineparser.o test/testcmdlineparser.cpp test/testcondition.o: test/testcondition.cpp lib/cxx11emu.h lib/checkcondition.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcondition.o test/testcondition.cpp test/testconstructors.o: test/testconstructors.cpp lib/cxx11emu.h lib/checkclass.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testconstructors.o test/testconstructors.cpp test/testcppcheck.o: test/testcppcheck.cpp lib/cxx11emu.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/cppcheck.h lib/analyzerinfo.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcppcheck.o test/testcppcheck.cpp test/testerrorlogger.o: test/testerrorlogger.cpp lib/cxx11emu.h lib/config.h lib/cppcheck.h lib/analyzerinfo.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testerrorlogger.o test/testerrorlogger.cpp test/testexceptionsafety.o: test/testexceptionsafety.cpp lib/cxx11emu.h lib/checkexceptionsafety.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testexceptionsafety.o test/testexceptionsafety.cpp test/testfilelister.o: test/testfilelister.cpp lib/cxx11emu.h lib/pathmatch.h lib/config.h test/testsuite.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testfilelister.o test/testfilelister.cpp test/testfunctions.o: test/testfunctions.cpp lib/cxx11emu.h lib/checkfunctions.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testfunctions.o test/testfunctions.cpp test/testgarbage.o: test/testgarbage.cpp lib/cxx11emu.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testgarbage.o test/testgarbage.cpp test/testimportproject.o: test/testimportproject.cpp lib/cxx11emu.h lib/importproject.h lib/config.h lib/platform.h lib/utils.h test/testsuite.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testimportproject.o test/testimportproject.cpp test/testincompletestatement.o: test/testincompletestatement.cpp lib/cxx11emu.h lib/checkother.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testincompletestatement.o test/testincompletestatement.cpp test/testinternal.o: test/testinternal.cpp lib/cxx11emu.h lib/tokenize.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/checkinternal.h lib/check.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testinternal.o test/testinternal.cpp test/testio.o: test/testio.cpp lib/cxx11emu.h lib/checkio.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testio.o test/testio.cpp test/testleakautovar.o: test/testleakautovar.cpp lib/cxx11emu.h lib/checkleakautovar.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testleakautovar.o test/testleakautovar.cpp test/testlibrary.o: test/testlibrary.cpp lib/cxx11emu.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/library.h lib/mathlib.h lib/standards.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testlibrary.o test/testlibrary.cpp test/testmathlib.o: test/testmathlib.cpp lib/cxx11emu.h lib/mathlib.h lib/config.h test/testsuite.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testmathlib.o test/testmathlib.cpp test/testmemleak.o: test/testmemleak.cpp lib/cxx11emu.h lib/checkmemoryleak.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/preprocessor.h lib/symboldatabase.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testmemleak.o test/testmemleak.cpp test/testnullpointer.o: test/testnullpointer.cpp lib/cxx11emu.h lib/checknullpointer.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testnullpointer.o test/testnullpointer.cpp test/testoptions.o: test/testoptions.cpp lib/cxx11emu.h test/options.h test/testsuite.h lib/config.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testoptions.o test/testoptions.cpp test/testother.o: test/testother.cpp lib/cxx11emu.h lib/checkother.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testother.o test/testother.cpp test/testpath.o: test/testpath.cpp lib/cxx11emu.h lib/path.h lib/config.h test/testsuite.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpath.o test/testpath.cpp test/testpathmatch.o: test/testpathmatch.cpp lib/cxx11emu.h lib/pathmatch.h lib/config.h test/testsuite.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpathmatch.o test/testpathmatch.cpp test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/cxx11emu.h lib/checkpostfixoperator.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpostfixoperator.o test/testpostfixoperator.cpp test/testpreprocessor.o: test/testpreprocessor.cpp lib/cxx11emu.h lib/platform.h lib/config.h lib/preprocessor.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpreprocessor.o test/testpreprocessor.cpp test/testrunner.o: test/testrunner.cpp lib/cxx11emu.h test/options.h lib/preprocessor.h lib/config.h test/testsuite.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testrunner.o test/testrunner.cpp test/testsamples.o: test/testsamples.cpp lib/cxx11emu.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/cppcheck.h lib/analyzerinfo.h lib/importproject.h lib/platform.h lib/utils.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/path.h lib/pathmatch.h test/redirect.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsamples.o test/testsamples.cpp test/testsimplifytemplate.o: test/testsimplifytemplate.cpp lib/cxx11emu.h lib/config.h lib/platform.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/templatesimplifier.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytemplate.o test/testsimplifytemplate.cpp test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/cxx11emu.h lib/platform.h lib/config.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytokens.o test/testsimplifytokens.cpp test/testsimplifytypedef.o: test/testsimplifytypedef.cpp lib/cxx11emu.h lib/platform.h lib/config.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytypedef.o test/testsimplifytypedef.cpp test/testsizeof.o: test/testsizeof.cpp lib/cxx11emu.h lib/checksizeof.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsizeof.o test/testsizeof.cpp test/teststl.o: test/teststl.cpp lib/cxx11emu.h lib/checkstl.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/teststl.o test/teststl.cpp test/teststring.o: test/teststring.cpp lib/cxx11emu.h lib/checkstring.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/teststring.o test/teststring.cpp test/testsuite.o: test/testsuite.cpp lib/cxx11emu.h test/testsuite.h lib/config.h lib/errorlogger.h lib/suppressions.h test/options.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuite.o test/testsuite.cpp test/testsuppressions.o: test/testsuppressions.cpp lib/cxx11emu.h lib/config.h lib/cppcheck.h lib/analyzerinfo.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/check.h lib/settings.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuppressions.o test/testsuppressions.cpp test/testsymboldatabase.o: test/testsymboldatabase.cpp lib/cxx11emu.h lib/library.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/standards.h lib/platform.h lib/settings.h lib/importproject.h lib/utils.h lib/timer.h lib/symboldatabase.h lib/token.h lib/valueflow.h test/testsuite.h test/testutils.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp test/testthreadexecutor.o: test/testthreadexecutor.cpp lib/cxx11emu.h lib/settings.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testthreadexecutor.o test/testthreadexecutor.cpp test/testtimer.o: test/testtimer.cpp lib/cxx11emu.h test/testsuite.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtimer.o test/testtimer.cpp test/testtoken.o: test/testtoken.cpp lib/cxx11emu.h lib/settings.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h test/testutils.h lib/tokenize.h lib/tokenlist.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtoken.o test/testtoken.cpp test/testtokenize.o: test/testtokenize.cpp lib/cxx11emu.h lib/config.h lib/platform.h lib/preprocessor.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenize.o test/testtokenize.cpp test/testtokenlist.o: test/testtokenlist.cpp lib/cxx11emu.h lib/settings.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenlist.o test/testtokenlist.cpp test/testtype.o: test/testtype.cpp lib/cxx11emu.h lib/checktype.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtype.o test/testtype.cpp test/testuninitvar.o: test/testuninitvar.cpp lib/cxx11emu.h lib/checkuninitvar.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testuninitvar.o test/testuninitvar.cpp test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/cxx11emu.h lib/checkunusedfunctions.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedfunctions.o test/testunusedfunctions.cpp test/testunusedprivfunc.o: test/testunusedprivfunc.cpp lib/cxx11emu.h lib/checkclass.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedprivfunc.o test/testunusedprivfunc.cpp test/testunusedvar.o: test/testunusedvar.cpp lib/cxx11emu.h lib/checkunusedvar.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedvar.o test/testunusedvar.cpp test/testvaarg.o: test/testvaarg.cpp lib/cxx11emu.h lib/checkvaarg.h lib/check.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/settings.h lib/importproject.h lib/platform.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvaarg.o test/testvaarg.cpp test/testvalueflow.o: test/testvalueflow.cpp lib/cxx11emu.h lib/library.h lib/config.h lib/errorlogger.h lib/suppressions.h lib/mathlib.h lib/standards.h lib/platform.h lib/settings.h lib/importproject.h lib/utils.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvalueflow.o test/testvalueflow.cpp test/testvarid.o: test/testvarid.cpp lib/cxx11emu.h lib/platform.h lib/config.h lib/settings.h lib/errorlogger.h lib/suppressions.h lib/importproject.h lib/utils.h lib/library.h lib/mathlib.h lib/standards.h lib/timer.h test/testsuite.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvarid.o test/testvarid.cpp externals/simplecpp/simplecpp.o: externals/simplecpp/simplecpp.cpp lib/cxx11emu.h externals/simplecpp/simplecpp.h $(CXX) $(CPPFLAGS) $(CFG) $(CXXFLAGS) -w $(UNDEF_STRICT_ANSI) -c -o externals/simplecpp/simplecpp.o externals/simplecpp/simplecpp.cpp externals/tinyxml/tinyxml2.o: externals/tinyxml/tinyxml2.cpp lib/cxx11emu.h externals/tinyxml/tinyxml2.h $(CXX) $(CPPFLAGS) $(CFG) $(CXXFLAGS) -w $(UNDEF_STRICT_ANSI) -c -o externals/tinyxml/tinyxml2.o externals/tinyxml/tinyxml2.cpp tools/dmake.o: tools/dmake.cpp lib/cxx11emu.h cli/filelister.h lib/pathmatch.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/dmake.o tools/dmake.cpp tools/reduce.o: tools/reduce.cpp lib/cxx11emu.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/reduce.o tools/reduce.cpp cppcheck-1.82/addons/000077500000000000000000000000001322667425100145045ustar00rootroot00000000000000cppcheck-1.82/addons/cert-test.c000066400000000000000000000004651322667425100165670ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump cert-test.c && python cert.py -verify cert-test.c.dump 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 } cppcheck-1.82/addons/cert.py000066400000000000000000000147711322667425100160250ustar00rootroot00000000000000#!/usr/bin/env python # # Cert: Some extra CERT checkers # # Cppcheck itself handles many CERT rules. Cppcheck warns when there is undefined behaviour. # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python cert.py main.cpp.dump 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) + ':' + id) else: sys.stderr.write( '[' + token.file + ':' + str(token.linenr) + '] (' + severity + '): ' + msg + ' [' + id + ']\n') 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(var): decl = var.typeStartToken while decl and decl.isName: if decl.str == 'struct': structScope = decl.next.typeScope if structScope: linenr = int(structScope.classStart.linenr) for line in open(structScope.classStart.file): linenr -= 1 if linenr == 0: return True if re.match(r'#pragma\s+pack\s*\(', line): return False break decl = decl.next return False 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.variable) 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 # EXP42-C # do not compare padding data def exp42(data): for token in data.tokenlist: if token.str != '(' or not token.astOperand1: 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 token.astOperand1.str == 'memcmp' and (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)", 'cert-EXP42-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', 'cert-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 < 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 + ')', 'cert-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 < 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 + ')', 'cert-INT31-c') break for arg in sys.argv[1:]: if arg == '-verify': VERIFY = True continue print('Checking ' + arg + '...') data = cppcheckdata.parsedump(arg) 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: print('Checking ' + arg + ', config "' + cfg.name + '"...') exp42(cfg) exp46(cfg) int31(cfg, data.platform) 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.82/addons/cppcheckdata.doxyfile000066400000000000000000002250471322667425100206750ustar00rootroot00000000000000# 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.82/addons/cppcheckdata.py000066400000000000000000000550751322667425100175040ustar00rootroot00000000000000""" cppcheckdata This is a Python module that helps you access Cppcheck dump data. License: No restrictions, use this as you need. """ import xml.etree.ElementTree as ET import argparse 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 def __init__(self, element): self.str = element.get('str') self.file = element.get('file') self.linenr = element.get('linenr') class ValueType: """ ValueType class. Contains (promoted) type information for each node in the AST. """ type = None sign = None 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') 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 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'} 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 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 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 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 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 self.linkId = element.get('link') self.link = None self.varId = 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 = element.get('linenr') 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 classStart The { Token for this scope classEnd 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. type Type of scope: Global, Function, Class, If, While """ Id = None classStartId = None classStart = None classEndId = None classEnd = None className = None nestedInId = None nestedIn = None type = None def __init__(self, element): self.Id = element.get('id') self.className = element.get('className') self.classStartId = element.get('classStart') self.classStart = None self.classEndId = element.get('classEnd') self.classEnd = None self.nestedInId = element.get('nestedIn') self.nestedIn = None self.type = element.get('type') def setId(self, IdMap): self.classStart = IdMap[self.classStartId] self.classEnd = IdMap[self.classEndId] self.nestedIn = IdMap[self.nestedInId] class Function: """ Information about a function C++ class: http://cppcheck.net/devinfo/doxyoutput/classFunction.html """ Id = None argument = None argumentId = None tokenDef = None tokenDefId = None name = None def __init__(self, element): self.Id = element.get('id') self.tokenDefId = element.get('tokenDef') self.name = element.get('name') self.argument = {} self.argumentId = {} for arg in element: self.argumentId[arg.get('nr')] = arg.get('id') 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 isArgument Is this variable a function argument? isArray Is this variable an array? isClass Is this variable a class or struct? 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? """ Id = None nameTokenId = None nameToken = None typeStartTokenId = None typeStartToken = None typeEndTokenId = None typeEndToken = None isArgument = False isArray = False isClass = False isExtern = False isLocal = False isPointer = False isReference = False isStatic = False 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.isArgument = element.get('isArgument') == 'true' self.isArray = element.get('isArray') == 'true' self.isClass = element.get('isClass') == 'true' 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' def setId(self, IdMap): self.nameToken = IdMap[self.nameTokenId] self.typeStartToken = IdMap[self.typeStartTokenId] self.typeEndToken = IdMap[self.typeEndTokenId] 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 condition condition where this Value comes from """ intvalue = None tokvalue = None condition = None def __init__(self, element): self.intvalue = element.get('intvalue') if self.intvalue: self.intvalue = int(self.intvalue) self.tokvalue = element.get('tokvalue') self.condition = element.get('condition-line') if self.condition: self.condition = int(self.condition) def __init__(self, element): self.Id = element.get('id') self.values = [] for value in element: self.values.append(ValueFlow.Value(value)) class Configuration: """ Configuration class This class contains the directives, tokens, scopes, functions, variables and value flows 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 = [] for element in confignode: 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: self.variables.append(Variable(variable)) 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 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) 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')) 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 = [] def __init__(self, filename): self.configurations = [] data = ET.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] # 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 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") return parser def reportError(template, callstack=(), severity='', message='', id=''): """ Format an error message according to the template. :param template: format string, or 'gcc', 'vs' or 'edit'. :param callstack: e.g. [['file1.cpp',10],['file2.h','20'], ... ] :param severity: e.g. 'error', 'warning' ... :param id: message ID. :param message: message text. """ # expand predefined templates if template == 'gcc': template = '{file}:{line}: {severity}: {message}' elif template == 'vs': template = '{file}({line}): {severity}: {message}' elif template == 'edit': template = '{file} +{line}: {severity}: {message}' # compute 'callstack}, {file} and {line} replacements stack = ' -> '.join('[' + f + ':' + str(l) + ']' for (f, l) in callstack) file = callstack[-1][0] line = str(callstack[-1][1]) # format message return template.format(callstack=stack, file=file, line=line, severity=severity, message=message, id=id) cppcheck-1.82/addons/findcasts.py000066400000000000000000000023021322667425100170310ustar00rootroot00000000000000#!/usr/bin/env python # # Locate casts in the code # import cppcheckdata import sys messages = set() for arg in sys.argv[1:]: 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 # 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 msg = '[' + token.file + ':' + str( token.linenr) + '] (information) findcasts.py: found a cast\n' if msg not in messages: messages.add(msg) sys.stderr.write(msg) cppcheck-1.82/addons/misra-test.c000066400000000000000000000114261322667425100167440ustar00rootroot00000000000000// 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" // 20.3 #include // 21.4 #include // 21.5 #include // 21.11 typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; //// 3.1 void misra_5_1() { int a123456789012345678901234567890; // no-warning int a1234567890123456789012345678901; // 5.1 } void misra_5_3() { u8 x=1; if (y!=0) { u8 x=2; // 5.3 } else {} } #define m54_123456789012345678901234567890123456789012345678901234567890 1 // 5.4 #define m54_1234567890123456789012345678901234567890123456789012345678901 2 // 5.4 #define m55(x,y) (x+y) int m55; // 5.5 void misra_7_1() { int x = 066; // 7.1 } void misra_7_3() { int x = 12l; // 7.3 int x = 12lu; // 7.3 } extern int a811[]; // 8.11 enum e812 { A=3, B=3 // 8.12 }; void misra_8_14(char * restrict str) {} // 8.14 void misra_9_5() { int x[] = {[0]=23}; // 9.5 } void misra_10_4(u8 x, u16 y) { z = x + y; // 10.4 } void misra_10_6(u8 x) { u16 y = x+x; // 10.6 } void misra_10_8(u8 x) { y = (u16)x; y = (u16)(x+x); // 10.8 } void misra_11_3(u8* p) { x = (u64*)p; // 11.3 } void misra_11_4(u8*p) { u64 y = (u64)p; // 11.4 } void misra_11_5(void *p) { x = (u8 *)p; // 11.5 } void misra_11_6() { void *p; p = (void*)123; // 11.6 x = (u64)p; // 11.6 } struct Fred {}; struct Wilma {}; void misra_11_7(struct Fred *fred) { struct Wilma *wilma = (struct Wilma *)fred; // 11.7 } char * misra_11_8(const char *str) { return (char *)str; // 11.8 } #define MISRA_11_9 ((void*)0) // 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 } void misra_12_3() { f((1,2),3); // TODO for (i=0;i<10;i++,j++){} // 12.3 } void misra_12_4() { x = 123456u * 123456u; // 12.4 } void misra_13_1(int *p) { volatile int v; int a[3] = {0, (*p)++, 2}; // 13.1 int b[2] = {v,1}; // TODO } void misra_13_3() { x = y++; // 13.3 } void misra_13_4() { if (x != (y = z)) {} // 13.4 else {} } void misra_13_5() { if (x && (y++ < 123)){} // 13.5 else {} } void misra_13_6() { return sizeof(x++); // 13.6 } void misra_14_1() { for (float f=0.1f; f<1.0f; f += 0.1f){} // 14.1 } void misra_14_2() { for (dostuff();a<10;a++) {} // 14.2 for (;i++<10;) {} // 14.2 for (;i<10;dostuff()) {} // TODO // TODO check more variants } void misra_14_4() { if (x+4){} // 14.4 else {} } 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() { if (x!=0){} // no-warning if (x!=0){} else if(x==1){} // 15.7 if (x!=0){} else if(x==1){}else{;} // 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; default: break; } } 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;} } } 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_6(int x[static 20]) {} // 17.6 void misra_17_8(int x) { x = 3; // 17.8 } void misra_18_5() { int *** p; // 18.5 } void misra_18_8(int x) { int buf1[10]; int buf2[sizeof(int)]; int vla[x]; // 18.8 } union misra_19_2 { }; // 19.2 #include "notfound.h" // 20.1 #define int short // 20.4 #undef X // 20.5 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() { atof(str); // 21.7 atoi(str); // 21.7 atol(str); // 21.7 atoll(str); // 21.7 } void misra_21_8() { abort(); // 21.8 getenv("foo"); // 21.8 system(""); // 21.8 } void misra_21_9() { bsearch(key,base,num,size,cmp); // 21.9 qsort(base,num,size,cmp); // 21.9 } cppcheck-1.82/addons/misra.py000066400000000000000000001010571322667425100161750ustar00rootroot00000000000000#!/usr/bin/env python # # MISRA C 2012 checkers # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python misra.py 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 # # Total number of rules: 153 import cppcheckdata import sys import re ruleTexts = {} VERIFY = False VERIFY_EXPECTED = [] VERIFY_ACTUAL = [] def reportError(location, num1, num2): if VERIFY: VERIFY_ACTUAL.append(str(location.linenr) + ':' + str(num1) + '.' + str(num2)) else: num = num1 * 100 + num2 id = 'misra-c2012-' + str(num1) + '.' + str(num2) if num in ruleTexts: errmsg = ruleTexts[num] + ' [' + id + ']' else: errmsg = 'misra violation (use --rule-texts= to get proper output) [' + id + ']' sys.stderr.write('[' + location.file + ':' + str(location.linenr) + '] ' + errmsg + '\n') def simpleMatch(token, pattern): for p in pattern.split(' '): if not token or token.str != p: return False token = token.next return True 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 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 {'+', '-', '*', '/', '%', '&', '|', '^'}: 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 return None def bitsOfEssentialType(expr): type = getEssentialType(expr) if type is None: return 0 if type == 'char': return CHAR_BIT if type == 'short': return SHORT_BIT if type == 'int': return INT_BIT if type == 'long': return LONG_BIT if type == 'long long': return LONG_LONG_BIT 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 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 hasFloatComparison(expr): if not expr: return False if expr.isLogicalOp: return hasFloatComparison(expr.astOperand1) or hasFloatComparison(expr.astOperand2) if expr.isComparisonOp: # TODO: Use ValueType return cppcheckdata.astIsFloat(expr.astOperand1) or cppcheckdata.astIsFloat(expr.astOperand2) return False def hasSideEffectsRecursive(expr): if not expr: 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 in {'++', '--', '='}: return True # Todo: Check function calls return hasSideEffectsRecursive(expr.astOperand1) or hasSideEffectsRecursive(expr.astOperand2) def isBoolExpression(expr): return expr and expr.str in {'!', '==', '!=', '<', '<=', '>', '>=', '&&', '||'} 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): # TODO this function is very incomplete. use ValueType? if not expr: return False if expr.isNumber: return 'u' in expr.str or 'U' in expr.str if expr.str in {'+', '-', '*', '/', '%'}: return isUnsignedInt(expr.astOperand1) or isUnsignedInt(expr.astOperand2) return False 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 noParentheses(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 def misra_3_1(rawTokens): for token in rawTokens: if token.str.startswith('/*') or token.str.startswith('//'): if '//' in token.str[2:] or '/*' in token.str[2:]: reportError(token, 3, 1) def misra_5_1(data): for token in data.tokenlist: if token.isName and len(token.str) > 31: reportError(token, 5, 1) def misra_5_3(data): scopeVars = {} for var in data.variables: if var.isArgument: # TODO continue if var.nameToken.scope not in scopeVars: scopeVars[var.nameToken.scope] = [] scopeVars[var.nameToken.scope].append(var) for innerScope in data.scopes: if innerScope.type == 'Global': continue if innerScope not in scopeVars: continue for innerVar in scopeVars[innerScope]: outerScope = innerScope.nestedIn while outerScope: if outerScope not in scopeVars: outerScope = outerScope.nestedIn continue found = False for outerVar in scopeVars[outerScope]: if innerVar.nameToken.str == outerVar.nameToken.str: found = True break if found: reportError(innerVar.nameToken, 5, 3) break outerScope = outerScope.nestedIn def misra_5_4(data): for dir in data.directives: if re.match(r'#define [a-zA-Z0-9_]{64,}', dir.str): reportError(dir, 5, 4) def misra_5_5(data): macroNames = [] for dir in data.directives: res = re.match(r'#define ([A-Za-z0-9_]+)', dir.str) if res: macroNames.append(res.group(1)) for var in data.variables: if var.nameToken.str in macroNames: reportError(var.nameToken, 5, 5) def misra_7_1(rawTokens): for tok in rawTokens: if re.match(r'^0[0-7]+$', tok.str): reportError(tok, 7, 1) def misra_7_3(rawTokens): for tok in rawTokens: if re.match(r'^[0-9]+l', tok.str): reportError(tok, 7, 3) def misra_8_11(data): for var in data.variables: if var.isExtern and simpleMatch(var.nameToken.next, '[ ]') and var.nameToken.scope.type == 'Global': reportError(var.nameToken, 8, 11) def misra_8_12(data): for token in data.tokenlist: if token.str != '{': continue if not token.scope or token.scope.type != 'Enum': continue etok = token values = [] while etok: if etok.str == '}': break if etok.str == '=': rhsValues = etok.astOperand2.values if rhsValues and len(rhsValues) == 1: if rhsValues[0].intvalue in values: reportError(etok, 8, 12) break values.append(rhsValues[0].intvalue) etok = etok.next def misra_8_14(rawTokens): for token in rawTokens: if token.str == 'restrict': reportError(token, 8, 14) def misra_9_5(rawTokens): for token in rawTokens: if simpleMatch(token, '[ ] = { ['): reportError(token, 9, 5) def misra_10_4(data): for token in data.tokenlist: if token.str not in {'+', '-', '*', '/', '%', '&', '|', '^'} 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 not token.astOperand1.valueType.isIntegral() or not token.astOperand2.valueType.isIntegral(): continue e1 = getEssentialType(token.astOperand1) e2 = getEssentialType(token.astOperand2) if e1 and e2 and e1 != e2: reportError(token, 10, 4) def misra_10_6(data): for token in data.tokenlist: if token.str != '=' or not token.astOperand1 or not 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) e = getEssentialType(token.astOperand2) if not e: continue index2 = intTypes.index(e) if index1 > index2: reportError(token, 10, 6) except ValueError: pass def misra_10_8(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 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: reportError(token, 10, 8) except ValueError: pass def misra_11_3(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.pointer == vt2.pointer and vt1.pointer > 0 and vt1.type != vt2.type and\ vt1.isIntegral() and vt2.isIntegral() and vt1.type != 'char': reportError(token, 11, 3) def misra_11_4(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.pointer == 0 and vt2.pointer > 0 and vt2.type != 'void': reportError(token, 11, 4) def misra_11_5(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.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': reportError(token, 11, 5) def misra_11_6(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.pointer == 1 and vt1.type == 'void' and vt2.pointer == 0: reportError(token, 11, 6) elif vt1.pointer == 0 and vt2.pointer == 1 and vt2.type == 'void': reportError(token, 11, 6) def misra_11_7(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.pointer > 0 and vt1.type == 'record' and\ vt2.pointer > 0 and vt2.type == 'record' and vt1.typeScopeId != vt2.typeScopeId: reportError(token, 11, 7) def misra_11_8(data): for token in data.tokenlist: if not isCast(token): continue if not token.valueType or not token.astOperand1.valueType: continue if token.valueType.pointer == 0 or token.valueType.pointer == 0: continue if token.valueType.constness == 0 and token.astOperand1.valueType.constness > 0: reportError(token, 11, 8) def misra_11_9(data): for directive in data.directives: res1 = re.match(r'#define ([A-Za-z_][A-Za-z_0-9]*) (.*)', directive.str) if not res1: continue name = res1.group(1) if name == 'NULL': continue value = res1.group(2).replace(' ', '') if value == '((void*)0)': reportError(directive, 11, 9) def misra_12_1_sizeof(rawTokens): state = 0 for tok in rawTokens: if tok.str.startswith('//') or tok.str.startswith('/*'): continue if tok.str == 'sizeof': state = 1 elif state == 1: if re.match(r'^[a-zA-Z_]', tok.str): state = 2 else: state = 0 elif state == 2: if tok.str in {'+', '-', '*', '/', '%'}: reportError(tok, 12, 1) else: state = 0 def misra_12_1(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 noParentheses(token.astOperand1, token): reportError(token, 12, 1) continue p2 = getPrecedence(token.astOperand2) if p < p2 <= 12 and noParentheses(token, token.astOperand2): reportError(token, 12, 1) continue def misra_12_2(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: reportError(token, 12, 2) def misra_12_3(data): for token in data.tokenlist: if token.str != ',' or token.scope.type == 'Enum': continue if token.astParent and token.astParent.str in {'(', ',', '{'}: continue reportError(token, 12, 3) def misra_12_4(data): if INT_BIT == 16: max_uint = 0xffff elif INT_BIT == 32: max_uint = 0xffffffff else: return for token in data.tokenlist: if (not isConstantExpression(token)) or (not isUnsignedInt(token)): continue if not token.values: continue for value in token.values: if value.intvalue < 0 or value.intvalue > max_uint: reportError(token, 12, 4) break def misra_13_1(data): for token in data.tokenlist: if token.str != '=': continue init = token.next if init and init.str == '{' and hasSideEffectsRecursive(init): reportError(init, 13, 1) def misra_13_3(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: reportError(astTop, 13, 3) def misra_13_4(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 [',', ';']): reportError(token, 13, 4) def misra_13_5(data): for token in data.tokenlist: if token.isLogicalOp and hasSideEffectsRecursive(token.astOperand2): reportError(token, 13, 5) def misra_13_6(data): for token in data.tokenlist: if token.str == 'sizeof' and hasSideEffectsRecursive(token.next): reportError(token, 13, 6) def misra_14_1(data): for token in data.tokenlist: if token.str != 'for': continue exprs = getForLoopExpressions(token) if exprs and hasFloatComparison(exprs[1]): reportError(token, 14, 1) def misra_14_2(data): for token in data.tokenlist: expressions = getForLoopExpressions(token) if not expressions: continue if expressions[0] and not expressions[0].isAssignmentOp: reportError(token, 14, 2) elif hasSideEffectsRecursive(expressions[1]): reportError(token, 14, 2) def misra_14_4(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): reportError(token, 14, 4) def misra_15_1(data): for token in data.tokenlist: if token.str == "goto": reportError(token, 15, 1) def misra_15_2(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): reportError(token, 15, 2) def misra_15_3(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: reportError(token, 15, 3) def misra_15_5(data): for token in data.tokenlist: if token.str == 'return' and token.scope.type != 'Function': reportError(token, 15, 5) def misra_15_6(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"): continue state = 1 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 != '{': reportError(tok1, 15, 6) def misra_15_7(data): for token in data.tokenlist: if not simpleMatch(token, '}'): continue if not token.scope.type == 'If': continue if not token.scope.nestedIn.type == 'Else': continue if not token.next.str == 'else': reportError(token, 15, 7) # TODO add 16.1 rule def misra_16_2(data): for token in data.tokenlist: if token.str == 'case' and token.scope.type != 'Switch': reportError(token, 16, 2) def misra_16_3(rawTokens): # state: 0=no, 1=break is seen but not its ';', 2=after 'break;', 'comment', '{' state = 0 for token in rawTokens: if token.str == 'break': state = 1 elif token.str == ';': if state == 1: state = 2 else: state = 0 elif token.str.startswith('/*') or token.str.startswith('//'): if 'fallthrough' in token.str.lower(): state = 2 elif token.str == '{': state = 2 elif token.str == 'case' and state != 2: reportError(token, 16, 3) def misra_16_4(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': reportError(token, 16, 4) def misra_16_5(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': reportError(token, 16, 5) def misra_16_6(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 == 'break': count = count + 1 elif tok.str == '{': tok = tok.link if simpleMatch(tok.previous.previous, 'break ;'): count = count + 1 elif tok.str == '}': break tok = tok.next if count < 2: reportError(token, 16, 6) def misra_16_7(data): for token in data.tokenlist: if simpleMatch(token, 'switch (') and isBoolExpression(token.next.astOperand2): reportError(token, 16, 7) def misra_17_1(data): for token in data.tokenlist: if isFunctionCall(token) and token.astOperand1.str in {'va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'}: reportError(token, 17, 1) def misra_17_6(rawTokens): for token in rawTokens: if simpleMatch(token, '[ static'): reportError(token, 17, 6) def misra_17_8(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: reportError(token, 17, 8) def misra_18_5(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: reportError(var.nameToken, 18, 5) def misra_18_8(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 if not isConstantExpression(typetok.astOperand2): reportError(var.nameToken, 18, 8) def misra_19_2(data): for token in data.tokenlist: if token.str == 'union': reportError(token, 19, 2) def misra_20_1(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): reportError(directive, 20, 1) break def misra_20_2(data): for directive in data.directives: if not directive.str.startswith('#include '): continue for pattern in {'\\', '//', '/*', "'"}: if pattern in directive.str: reportError(directive, 20, 2) break def misra_20_3(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 if not headerToken or not (headerToken.str.startswith('<') or headerToken.str.startswith('"')): reportError(token, 20, 3) def misra_20_4(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): reportError(directive, 20, 4) def misra_20_5(data): for directive in data.directives: if directive.str.startswith('#undef '): reportError(directive, 20, 5) def misra_21_3(data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in {'malloc', 'calloc', 'realloc', 'free'}): reportError(token, 21, 3) def misra_21_4(data): directive = findInclude(data.directives, '') if directive: reportError(directive, 21, 4) def misra_21_5(data): directive = findInclude(data.directives, '') if directive: reportError(directive, 21, 5) def misra_21_7(data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in {'atof', 'atoi', 'atol', 'atoll'}): reportError(token, 21, 7) def misra_21_8(data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in {'abort', 'getenv', 'system'}): reportError(token, 21, 8) def misra_21_9(data): for token in data.tokenlist: if (token.str in {'bsearch', 'qsort'}) and token.next and token.next.str == '(': reportError(token, 21, 9) def misra_21_11(data): directive = findInclude(data.directives, '') if directive: reportError(directive, 21, 11) def loadRuleTexts(filename): num1 = 0 num2 = 0 for line in open(filename, 'rt'): line = line.replace('\r', '').replace('\n', '') res = re.match(r'^Rule ([0-9]+).([0-9]+)', line) if res: num1 = int(res.group(1)) num2 = int(res.group(2)) continue res = re.match(r'^[ ]*(Advisory|Required|Mandatory)$', line) if res: continue res = re.match(r'^[ ]*([#A-Z].*)', line) if res: global ruleTexts ruleTexts[num1*100+num2] = res.group(1) num2 = num2 + 1 continue for arg in sys.argv[1:]: if arg == '-verify': VERIFY = True elif arg.startswith('--rule-texts='): loadRuleTexts(arg[13:]) for arg in sys.argv[1:]: if not arg.endswith('.dump'): continue data = cppcheckdata.parsedump(arg) CHAR_BIT = data.platform.char_bit SHORT_BIT = data.platform.short_bit INT_BIT = data.platform.int_bit LONG_BIT = data.platform.long_bit LONG_LONG_BIT = data.platform.long_long_bit POINTER_BIT = data.platform.pointer_bit 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'[0-9]+\.[0-9]+', word): VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word) else: print('Checking ' + arg + '...') cfgNumber = 0 for cfg in data.configurations: cfgNumber = cfgNumber + 1 if len(data.configurations) > 1: print('Checking ' + arg + ', config "' + cfg.name + '"...') if cfgNumber == 1: misra_3_1(data.rawTokens) misra_5_1(cfg) misra_5_3(cfg) misra_5_4(cfg) misra_5_5(cfg) if cfgNumber == 1: misra_7_1(data.rawTokens) misra_7_3(data.rawTokens) misra_8_11(cfg) misra_8_12(cfg) if cfgNumber == 1: misra_8_14(data.rawTokens) misra_9_5(data.rawTokens) misra_10_4(cfg) misra_10_6(cfg) misra_10_8(cfg) misra_11_3(cfg) misra_11_4(cfg) misra_11_5(cfg) misra_11_6(cfg) misra_11_7(cfg) misra_11_8(cfg) misra_11_9(cfg) if cfgNumber == 1: misra_12_1_sizeof(data.rawTokens) misra_12_1(cfg) misra_12_2(cfg) misra_12_3(cfg) misra_12_4(cfg) misra_13_1(cfg) misra_13_3(cfg) misra_13_4(cfg) misra_13_5(cfg) misra_13_6(cfg) misra_14_1(cfg) misra_14_2(cfg) misra_14_4(cfg) misra_15_1(cfg) misra_15_2(cfg) misra_15_3(cfg) misra_15_5(cfg) if cfgNumber == 1: misra_15_6(data.rawTokens) misra_15_7(cfg) misra_16_2(cfg) if cfgNumber == 1: misra_16_3(data.rawTokens) misra_16_4(cfg) misra_16_5(cfg) misra_16_6(cfg) misra_16_7(cfg) misra_17_1(cfg) if cfgNumber == 1: misra_17_6(data.rawTokens) misra_17_8(cfg) misra_18_5(cfg) misra_18_8(cfg) misra_19_2(cfg) misra_20_1(cfg) misra_20_2(cfg) if cfgNumber == 1: misra_20_3(data.rawTokens) misra_20_4(cfg) misra_20_5(cfg) misra_21_3(cfg) misra_21_4(cfg) misra_21_5(cfg) misra_21_7(cfg) misra_21_8(cfg) misra_21_9(cfg) misra_21_11(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.82/addons/naming.py000066400000000000000000000031761322667425100163360ustar00rootroot00000000000000#!/usr/bin/env python # # 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 RE_VARNAME = None RE_FUNCTIONNAME = None for arg in sys.argv[1:]: if arg[:6] == '--var=': RE_VARNAME = arg[6:] elif arg[:11] == '--function=': RE_FUNCTIONNAME = arg[11:] def reportError(token, severity, msg): sys.stderr.write( '[' + token.file + ':' + str(token.linenr) + '] (' + severity + ') naming.py: ' + msg + '\n') for arg in sys.argv[1:]: if not arg[-5:] == '.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: res = re.match(RE_VARNAME, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Variable ' + var.nameToken.str + ' violates naming convention') if RE_FUNCTIONNAME: for scope in cfg.scopes: if scope.type == 'Function': res = re.match(RE_FUNCTIONNAME, scope.className) if not res: reportError( scope.classStart, 'style', 'Function ' + scope.className + ' violates naming convention') cppcheck-1.82/addons/threadsafety.py000066400000000000000000000015161322667425100175440ustar00rootroot00000000000000#!/usr/bin/env python # # 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): sys.stderr.write( '[' + token.file + ':' + str(token.linenr) + '] (' + severity + '): ' + msg + ' [' + id + ']\n') def checkstatic(data): for var in data.variables: if var.isStatic and var.isLocal and var.isClass: reportError(var.typeStartToken, 'warning', ('Local static object: ' + var.nameToken.str), 'threadsafety') for arg in sys.argv[1:]: 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.82/addons/y2038/000077500000000000000000000000001322667425100152715ustar00rootroot00000000000000cppcheck-1.82/addons/y2038/README000066400000000000000000000144251322667425100161570ustar00rootroot00000000000000README of the Y2038 cppcheck addon ================================== Contents 1. What is Y2038? 2. What is the Y2038 cppcheck addon? 3. How does the Y2038 cppcheck addon work? 4. How to use the Y2038 cppcheck addon --- 1. What is Y2038? In a few words: In Linux, the current date and time is kept as the number of seconds elapsed since the Unix epoch, that is, since January 1st, 1970 at 00:00:00 GMT. Most of the time, this representation is stored as a 32-bit signed quantity. On January 19th, 2038 at 03:14:07 GMT, such 32-bit representations will reach their maximum positive value. What happens then is unpredictable: system time might roll back to December 13th, 1901 at 19:55:13, or it might keep running on until February 7th, 2106 at 06:28:15 GMT, or the computer may freeze, or just about anything you can think of, plus a few ones you can't. The workaround for this is to switch to a 64-bit signed representation of time as seconds from the Unix epoch. This representation will work for more than 250 billion years. Working around Y2038 requires fixing the Linux kernel, the C libraries, and any user code around which uses 32-bit epoch representations. There is Y2038-proofing work in progress on the Linux and GNU glibc front. 2. What is the Y2038 ccpcheck addon? The Y2038 cppcheck addon is a tool to help detect code which might need fixing because it is Y2038-unsafe. This may be because it uses types or functions from GNU libc or from the Linux kernel which are known not to be Y2038-proof. 3. How does the Y2038 cppcheck addon work? The Y2038 cppcheck addon takes XML dumps produced by cppcheck from source code files and looks for the names of types or functions which are known to be Y2038- unsafe, and emits diagnostics whenever it finds one. Of course, this is of little use if your code uses a Y2038-proof glibc and correctly configured Y2038-proof time support. This is why y2038.py takes into account two preprocessor directives: _TIME_BITS and __USE_TIME_BITS64. _TIME_BITS is defined equal to 64 by user code when it wants 64-bit time support from the GNU glibc. Code which does not define _TIME_BITS equal to 64 (or defines it to something else than 64) runs a risk of not being Y2038-proof. __USE_TIME_BITS64 is defined by the GNU glibc when it actually provides 64-bit time support. When this is defined, then all glibc symbols, barring bugs, are Y2038-proof (but your code might have its own Y2038 bugs, if it handles signed 32-bit Unix epoch values). The Y2038 cppcheck performs the following checks: 1. Upon meeting a definition for _TIME_BITS, if that definition does not set it equal to 64, this error diagnostic is emitted: Error: _TIME_BITS must be defined equal to 64 This case is very unlikely but might result from a typo, so pointing it out is quite useful. Note that definitions of _TIME_BITS as an expression evaluating to 64 will be flagged too. 2. Upon meeting a definition for _USE_TIME_BITS64, if _TIME_BITS is not defined equal to 64, this information diagnostic is emitted: Warning: _USE_TIME_BITS64 is defined but _TIME_BITS was not This reflects the fact that even though the glibc checked default to 64-bit time support, this was not requested by the user code, and therefore the user code might fail Y2038 if built against a glibc which defaults to 32-bit time support. 3. Upon meeting a symbol (type or function) which is known to be Y2038- unsafe, if _USE_TIME_BITS64 is undefined or _TIME_BITS not properly defined, this warning diagnostic is emitted: Warning: 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.82/addons/y2038/test/000077500000000000000000000000001322667425100162505ustar00rootroot00000000000000cppcheck-1.82/addons/y2038/test/y2038-inc.h000066400000000000000000000007771322667425100177700ustar00rootroot00000000000000#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.82/addons/y2038/test/y2038-test-1-bad-time-bits.c000066400000000000000000000004521322667425100227340ustar00rootroot00000000000000#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.82/addons/y2038/test/y2038-test-2-no-time-bits.c000066400000000000000000000004351322667425100226240ustar00rootroot00000000000000#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.82/addons/y2038/test/y2038-test-3-no-use-time-bits.c000066400000000000000000000004321322667425100234140ustar00rootroot00000000000000#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.82/addons/y2038/test/y2038-test-4-good.c000066400000000000000000000004331322667425100212450ustar00rootroot00000000000000/* * 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.82/addons/y2038/y2038.py000066400000000000000000000202001322667425100164220ustar00rootroot00000000000000#!/usr/bin/env python # # 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/ # $ y2038.py path-to-src/ # # y2038.py will walk the source tree for .dump files. import cppcheckdata import sys import os import re # -------------- # Error handling # -------------- diagnostics = {} def reportDiagnostic(template, configuration, file, line, severity, message): # collect diagnostics by configuration if configuration not in diagnostics: diagnostics[configuration] = [] # add error to this configuration diagnostics[configuration].append( cppcheckdata.reportError(template, [[file, line]], severity, message)) def printDiagnostics(): for cfg in diagnostics: sys.stderr.write('# Configuration "' + cfg + '":\n') for diag in diagnostics[cfg]: sys.stderr.write(diag + '\n') def reportDirDiag(template, cfg, filename, linenr, directive, severity, msg): reportDiagnostic(template, cfg.name, directive.file, directive.linenr, severity, msg) if (filename != directive.file) or (linenr != directive.linenr): reportDiagnostic(template, cfg.name, filename, linenr, 'information', directive.file + ' was included from here') def reportTokDiag(template, cfg, token, severity, msg): reportDiagnostic(template, cfg.name, token.file, token.linenr, severity, msg) # -------------------------------------------- # #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' } # return all files ending in .dump among or under the given paths def find_dump_files(paths): dumpfiles = [] for path in paths: if path.endswith('.dump'): if path not in dumpfiles: dumpfiles.append(path) else: for (top, subdirs, files) in os.walk(path): for file in files: if file.endswith('.dump'): f = top + '/' + file if f not in dumpfiles: dumpfiles.append(f) dumpfiles.sort() return dumpfiles # ----------------- # Let's get to work # ----------------- # extend cppcheck parser with our own options parser = cppcheckdata.ArgumentParser() parser.add_argument('-q', '--quiet', action='store_true', help='do not print "Checking ..." lines') parser.add_argument('paths', nargs='+', metavar='path', help='path to dump file or directory') # parse command line args = parser.parse_args() # now operate on each file in turn dumpfiles = find_dump_files(args.paths) for dumpfile in dumpfiles: if not args.quiet: print('Checking ' + dumpfile + '...') srcfile = dumpfile.rstrip('.dump') # at the start of the check, we don't know if code is Y2038 safe y2038safe = False # load XML from .dump file data = cppcheckdata.parsedump(dumpfile) # go through each configuration for cfg in data.configurations: if not args.quiet: print('Checking ' + dumpfile + ', 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): reportDirDiag(args.template, cfg, srcfile, srclinenr, directive, 'error', '_TIME_BITS must be defined equal to 64') time_bits_defined = 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: reportDirDiag(args.template, cfg, srcfile, srclinenr, directive, 'warning', '_USE_TIME_BITS64 is defined but _TIME_BITS was not') 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): reportTokDiag(args.template, cfg, token, 'warning', token.str + ' is Y2038-unsafe') token = token.next printDiagnostics() cppcheck-1.82/appveyor.yml000066400000000000000000000033551322667425100156320ustar00rootroot00000000000000version: 1.73.{build} clone_depth: 10 environment: matrix: - VisualStudioVersion: 10.0 platform: "Win32" configuration: "Debug" vcvarsall_platform: "x86" PlatformToolset: "v100" - VisualStudioVersion: 10.0 platform: "Win32" configuration: "Release" vcvarsall_platform: "x86" PlatformToolset: "v100" - VisualStudioVersion: 10.0 platform: "x64" configuration: "Debug" vcvarsall_platform: "x64" PlatformToolset: "v100" - VisualStudioVersion: 10.0 platform: "x64" configuration: "Release" vcvarsall_platform: "x64" PlatformToolset: "v100" - VisualStudioVersion: 14.0 platform: "Win32" configuration: "Debug" vcvarsall_platform: "x86" PlatformToolset: "v140" - VisualStudioVersion: 14.0 platform: "x64" configuration: "Debug" vcvarsall_platform: "x64" PlatformToolset: "v140" build_script: - ECHO Building %configuration% %platform% with MSVC %VisualStudioVersion% using %PlatformToolset% PlatformToolset - 'IF "%VisualStudioVersion%" == "10.0" IF "%platform%" == "x64" CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /%vcvarsall_platform% /%configuration%' - 'CALL "C:\Program Files (x86)\Microsoft Visual Studio %VisualStudioVersion%\VC\vcvarsall.bat" %vcvarsall_platform%' - 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" test_script: - IF "%CONFIGURATION%" == "Debug" bin\debug\testrunner.exe - IF "%CONFIGURATION%" == "Release" bin\testrunner.exe cppcheck-1.82/build-pcre.txt000066400000000000000000000035211322667425100160240ustar00rootroot00000000000000PCRE 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.82/build.bat000066400000000000000000000031311322667425100150210ustar00rootroot00000000000000@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.82/cfg/000077500000000000000000000000001322667425100137735ustar00rootroot00000000000000cppcheck-1.82/cfg/avr.cfg000066400000000000000000000306561322667425100152560ustar00rootroot00000000000000 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.82/cfg/bsd.cfg000066400000000000000000000067011322667425100152300ustar00rootroot00000000000000 false false false false false false false false false false false cppcheck-1.82/cfg/cppcheck-cfg.rng000066400000000000000000000350021322667425100170200ustar00rootroot00000000000000 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]*([ ]?[*])* error warning style performance portability information Obsolescent Obsolete c99 any variadic (-?[0-9]*[,:])*([-]?[0-9]+)? strlen argvalue sizeof mul first middle last [.][a-z]+ std-like array-like 1 2 4 8 s u 1 20 true false [a-zA-Z_][a-zA-Z_0-9]* [a-zA-Z_][a-zA-Z_0-9:,]* 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 cppcheck-1.82/cfg/cppcheck-lib.cfg000066400000000000000000000020461322667425100170020ustar00rootroot00000000000000 false false false false -50:50 cppcheck-1.82/cfg/embedded_sql.cfg000066400000000000000000000001551322667425100170650ustar00rootroot00000000000000 cppcheck-1.82/cfg/gnu.cfg000066400000000000000000000450401322667425100152500ustar00rootroot00000000000000 free get_current_dir_name false false false 0: false false false false false false false false false 0: false 0: false 0: false 0: false false 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 false false 1: false 1: close epoll_create close epoll_create1 false false false 1: false 1: close epoll_create close epoll_create1 cppcheck-1.82/cfg/gtk.cfg000066400000000000000000017464621322667425100152650ustar00rootroot00000000000000 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_malloc0 g_malloc0_n g_malloc_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_malloc0 g_try_malloc0_n g_try_malloc_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_new g_new0 g_try_new g_try_new0 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_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 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 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.82/cfg/microsoft_sal.cfg000066400000000000000000000415441322667425100173300ustar00rootroot00000000000000 cppcheck-1.82/cfg/motif.cfg000066400000000000000000000110401322667425100155660ustar00rootroot00000000000000 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 XtFree XtMalloc XtCalloc XtRealloc XtNew XtNewString false false false false XCloseDisplay XOpenDisplay cppcheck-1.82/cfg/posix.cfg000066400000000000000000003060231322667425100156220ustar00rootroot00000000000000 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 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 false false false false false false false false 0: false false false false false false false false false false false false 0: 0: false 0: 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 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'. false 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 false false false false false false false false false 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 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 true false 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 Non reentrant function 'ttyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ttyname_r'. 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 0: false false false 0: false false false false false false false 0: false 0: false 0: 0: false false 0: 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 strdup strndup wcsdup free mmap mmap64 munmap open creat socket close opendir fdopendir closedir fdopen fclose popen pclose mq_open mq_close getaddrinfo freeaddrinfo cppcheck-1.82/cfg/qt.cfg000066400000000000000000000343241322667425100151060ustar00rootroot00000000000000 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 0: false false 0,2:32 false false false cppcheck-1.82/cfg/sdl.cfg000066400000000000000000000034771322667425100152510ustar00rootroot00000000000000 false false false false false false SDL_FreeSurface SDL_CreateRGBSurface 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.82/cfg/sfml.cfg000066400000000000000000000220141322667425100154140ustar00rootroot00000000000000 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.82/cfg/std.cfg000066400000000000000000005154331322667425100152610ustar00rootroot00000000000000 true 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 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 1: 0: false false false false false false false false false false false false false false false false false false false false 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 false false false false false false false false false false false false 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: 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 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 0: strlen(arg1) false false 0: false 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 false 0: false false 0,2:36 false 0,2:36 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 false 0: false false 0: false 0: 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 0: false false false false 0: false false false false false false 0: 0: false false 0: false false 0: false 0: false 0: false 0: false 2:36 false false 0: false false false false false false false 0: false false false false malloc calloc free fopen tmpfile fclose cppcheck-1.82/cfg/windows.cfg000066400000000000000000003126451322667425100161610ustar00rootroot00000000000000 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 OpenFile CreateJobObject CreateRemoteThread CreateConsoleScreenBuffer OpenBackupEventLog OpenEventLog CreateFileMapping CreateFileMappingFromApp CreateFileMappingNuma CreateMemoryResourceNotification OpenFileMapping CreateNamedPipe CreateEvent CreateEventEx CreateMutex CreateSemaphore CreateTimerQueue CreateWaitableTimer OpenEvent OpenMutex OpenSemaphore OpenWaitableTimer OpenJobObject OpenProcess OpenThread CreateMailslot CloseHandle FindFirstFile FindFirstFileW FindFirstFileA FindFirstFileEx FindFirstFileExW FindFirstFileExA FindFirstFileNameW FindFirstFileNameTransactedW FindFirstStreamTransactedW FindFirstFileTransacted 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 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 wcsdup _strdup _wcsdup _mbsdup _tcsdup _malloc_dbg _aligned_malloc _aligned_malloc_dbg _aligned_offset_malloc _strdup_dbg _wcsdup_dbg _tcsdup_dbg free CoTaskMemAlloc CoTaskMemFree false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: false 0: true true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false false false false 0: false 0: false 0: 0: false 0: 0: false 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 arg1>0?arg1:-arg1 false false false false false false false false false false 0: false false false false false 0: false false false false false false false 0: 0: false false false false 0: false 0: false 0: false 0: false 0: false strlen(arg1) false strlen(arg1) false false false 0: false 0: false false false 0: false 0: false false false false false false false false false false 0 false false false true 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. cppcheck-1.82/cfg/wxwidgets.cfg000066400000000000000000001153721322667425100165120ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false 10,16 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 cppcheck-1.82/cli/000077500000000000000000000000001322667425100140035ustar00rootroot00000000000000cppcheck-1.82/cli/CMakeLists.txt000066400000000000000000000015251322667425100165460ustar00rootroot00000000000000include_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) target_link_libraries(cppcheck pcre) endif() if (MSVC) target_link_libraries(cppcheck Shlwapi.lib) endif() install(TARGETS cppcheck RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES ${cfgs} DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PROJECT_NAME}/ COMPONENT headers) cppcheck-1.82/cli/cli.vcxproj000066400000000000000000000624351322667425100162010ustar00rootroot00000000000000 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 8.1 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 ..\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 ..\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 ..\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 ..\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 ..\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 ..\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 ..\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 ..\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 {c183db5b-ad6c-423d-80ca-1f9549555a1a} cppcheck-1.82/cli/cli.vcxproj.filters000066400000000000000000000035441322667425100176440ustar00rootroot00000000000000 {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.82/cli/cmdlineparser.cpp000066400000000000000000001364551322667425100173550ustar00rootroot00000000000000/* * 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 "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.c_str()); Files = &Infile; } if (Files && *Files) { std::string FileName; while (std::getline(*Files, FileName)) { // next line if (!FileName.empty()) { PathNames.push_back(FileName); } } } } static void AddInclPathsToList(const std::string& FileList, std::list* PathNames) { // To keep things initially simple, if the file can't be opened, just be silent and move on. std::ifstream Files(FileList.c_str()); 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 (PathName.back() != '/') PathName += '/'; PathNames->push_back(PathName); } } } } static void AddPathsToSet(const std::string& FileName, std::set* set) { std::list templist; AddInclPathsToList(FileName, &templist); set->insert(templist.begin(), templist.end()); } CmdLineParser::CmdLineParser(Settings *settings) : _settings(settings) , _showHelp(false) , _showVersion(false) , _showErrorMessages(false) , _exitAfterPrint(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; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (std::strcmp(argv[i], "--version") == 0) { _showVersion = true; _exitAfterPrint = true; return true; } else if (std::strncmp(argv[i], "--cppcheck-build-dir=", 21) == 0) { _settings->buildDir = Path::fromNativeSeparators(argv[i] + 21); if (endsWith(_settings->buildDir, '/')) _settings->buildDir.erase(_settings->buildDir.size() - 1U); } // Flag used for various purposes during debugging else if (std::strcmp(argv[i], "--debug") == 0) _settings->debug = _settings->debugwarnings = true; // Show --debug output after the first simplifications else if (std::strcmp(argv[i], "--debug-normal") == 0) _settings->debugnormal = true; // Show debug warnings else if (std::strcmp(argv[i], "--debug-warnings") == 0) _settings->debugwarnings = true; // dump cppcheck data else if (std::strcmp(argv[i], "--dump") == 0) _settings->dump = true; // (Experimental) exception handling inside cppcheck client else if (std::strcmp(argv[i], "--exception-handling") == 0) _settings->exceptionHandling = true; else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) { _settings->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) _settings->inconclusive = 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") _settings->enforcedLang = Settings::C; else if (str == "c++") _settings->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.c_str()); if (!f.is_open()) { PrintMessage("cppcheck: Couldn't open the file: \"" + filename + "\"."); return false; } const std::string errmsg(_settings->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.c_str()); 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(_settings->nomsg.parseFile(f)); if (!errmsg.empty()) { PrintMessage(errmsg); return false; } } else if (std::strncmp(argv[i], "--suppress=", 11) == 0) { std::string suppression = argv[i]+11; const std::string errmsg(_settings->nomsg.addSuppressionLine(suppression)); if (!errmsg.empty()) { PrintMessage(errmsg); return false; } } // Enables inline suppressions. else if (std::strcmp(argv[i], "--inline-suppr") == 0) _settings->inlineSuppressions = true; // Verbose error messages (configuration info) else if (std::strcmp(argv[i], "-v") == 0 || std::strcmp(argv[i], "--verbose") == 0) _settings->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) _settings->force = true; // Output relative paths else if (std::strcmp(argv[i], "-rp") == 0 || std::strcmp(argv[i], "--relative-paths") == 0) _settings->relativePaths = true; else if (std::strncmp(argv[i], "-rp=", 4) == 0 || std::strncmp(argv[i], "--relative-paths=", 17) == 0) { _settings->relativePaths = true; if (argv[i][argv[i][3]=='='?4:17] != 0) { std::string paths = argv[i]+(argv[i][3]=='='?4:17); for (;;) { std::string::size_type pos = paths.find(';'); if (pos == std::string::npos) { _settings->basePaths.push_back(Path::fromNativeSeparators(paths)); break; } else { _settings->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) _settings->outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14)); // Write results in results.plist else if (std::strncmp(argv[i], "--plist-output=", 15) == 0) { _settings->plistOutput = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 15)); if (_settings->plistOutput.empty()) _settings->plistOutput = "./"; else if (!endsWith(_settings->plistOutput,'/')) _settings->plistOutput += '/'; } // Write results in results.xml else if (std::strcmp(argv[i], "--xml") == 0) _settings->xml = true; // Define the XML file version (and enable XML output) else if (std::strncmp(argv[i], "--xml-version=", 14) == 0) { std::string numberString(argv[i]+14); std::istringstream iss(numberString); if (!(iss >> _settings->xml_version)) { PrintMessage("cppcheck: argument to '--xml-version' is not a number."); return false; } if (_settings->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 _settings->xml = true; } // Only print something when there are errors else if (std::strcmp(argv[i], "-q") == 0 || std::strcmp(argv[i], "--quiet") == 0) _settings->quiet = true; // Check configuration else if (std::strcmp(argv[i], "--check-config") == 0) { _settings->checkConfiguration = true; } // Check library definitions else if (std::strcmp(argv[i], "--check-library") == 0) { _settings->checkLibrary = true; } else if (std::strncmp(argv[i], "--enable=", 9) == 0) { const std::string errmsg = _settings->addEnabled(argv[i] + 9); if (!errmsg.empty()) { PrintMessage(errmsg); return false; } // when "style" is enabled, also enable "warning", "performance" and "portability" if (_settings->isEnabled(Settings::STYLE)) { _settings->addEnabled("warning"); _settings->addEnabled("performance"); _settings->addEnabled("portability"); } } // --error-exitcode=1 else if (std::strncmp(argv[i], "--error-exitcode=", 17) == 0) { std::string temp = argv[i]+17; std::istringstream iss(temp); if (!(iss >> _settings->exitCode)) { _settings->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 (!_settings->userDefines.empty()) _settings->userDefines += ";"; _settings->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]; } _settings->userUndefs.insert(undef); } // -E else if (std::strcmp(argv[i], "-E") == 0) { _settings->preprocessOnly = 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 (path.back() != '/') path += '/'; _settings->includePaths.push_back(path); } else if (std::strncmp(argv[i], "--include=", 10) == 0) { std::string path = argv[i] + 10; path = Path::fromNativeSeparators(path); _settings->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) AddInclPathsToList(16 + argv[i], &_settings->includePaths); } else if (std::strncmp(argv[i], "--config-exclude=",17) ==0) { std::string path = argv[i] + 17; path = Path::fromNativeSeparators(path); _settings->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) AddPathsToSet(23 + argv[i], &_settings->configExcludePaths); } // 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], _pathnames); } // 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 (path.back() != '/') path += '/'; } _ignoredPaths.push_back(path); } } // --library else if (std::strncmp(argv[i], "--library=", 10) == 0) { if (!CppCheckExecutor::tryLoadLibrary(_settings->library, argv[0], argv[i]+10)) return false; } // --project else if (std::strncmp(argv[i], "--project=", 10) == 0) { _settings->project.import(argv[i]+10); if (std::strstr(argv[i], ".sln") || std::strstr(argv[i], ".vcxproj")) { if (!CppCheckExecutor::tryLoadLibrary(_settings->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; } } } // Report progress else if (std::strcmp(argv[i], "--report-progress") == 0) { _settings->reportProgress = true; } // --std else if (std::strcmp(argv[i], "--std=posix") == 0) { _settings->standards.posix = true; } else if (std::strcmp(argv[i], "--std=c89") == 0) { _settings->standards.c = Standards::C89; } else if (std::strcmp(argv[i], "--std=c99") == 0) { _settings->standards.c = Standards::C99; } else if (std::strcmp(argv[i], "--std=c11") == 0) { _settings->standards.c = Standards::C11; } else if (std::strcmp(argv[i], "--std=c++03") == 0) { _settings->standards.cpp = Standards::CPP03; } else if (std::strcmp(argv[i], "--std=c++11") == 0) { _settings->standards.cpp = Standards::CPP11; } else if (std::strcmp(argv[i], "--std=c++14") == 0) { _settings->standards.cpp = Standards::CPP14; } // Output formatter else if (std::strcmp(argv[i], "--template") == 0 || std::strncmp(argv[i], "--template=", 11) == 0) { // "--template path/" if (argv[i][10] == '=') _settings->outputFormat = argv[i] + 11; else if ((i+1) < argc && argv[i+1][0] != '-') { ++i; _settings->outputFormat = argv[i]; } else { PrintMessage("cppcheck: argument to '--template' is missing."); return false; } if (_settings->outputFormat == "gcc") _settings->outputFormat = "{file}:{line}: {severity}: {message}"; else if (_settings->outputFormat == "vs") _settings->outputFormat = "{file}({line}): {severity}: {message}"; else if (_settings->outputFormat == "edit") _settings->outputFormat = "{file} +{line}: {severity}: {message}"; } // 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 >> _settings->jobs)) { PrintMessage("cppcheck: argument to '-j' is not a number."); return false; } if (_settings->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 >> _settings->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) { _showErrorMessages = true; _settings->xml = true; _exitAfterPrint = true; } // documentation.. else if (std::strcmp(argv[i], "--doc") == 0) { std::ostringstream doc; // Get documentation.. for (std::list::iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { 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(); _exitAfterPrint = 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") _settings->showtime = SHOWTIME_FILE; else if (showtimeMode == "summary") _settings->showtime = SHOWTIME_SUMMARY; else if (showtimeMode == "top5") _settings->showtime = SHOWTIME_TOP5; else if (showtimeMode.empty()) _settings->showtime = 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]; _settings->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()) _settings->rules.push_back(rule); } } } #endif // Specify platform else if (std::strncmp(argv[i], "--platform=", 11) == 0) { std::string platform(11+argv[i]); if (platform == "win32A") _settings->platform(Settings::Win32A); else if (platform == "win32W") _settings->platform(Settings::Win32W); else if (platform == "win64") _settings->platform(Settings::Win64); else if (platform == "unix32") _settings->platform(Settings::Unix32); else if (platform == "unix64") _settings->platform(Settings::Unix64); else if (platform == "native") _settings->platform(Settings::Native); else if (platform == "unspecified") _settings->platform(Settings::Unspecified); else if (!_settings->platformFile(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) { _settings->force = false; std::istringstream iss(14+argv[i]); if (!(iss >> _settings->maxConfigs)) { PrintMessage("cppcheck: argument to '--max-configs=' is not a number."); return false; } if (_settings->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) { _pathnames.clear(); _showHelp = true; _exitAfterPrint = 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); _pathnames.push_back(path); } } _settings->project.ignorePaths(_ignoredPaths); if (_settings->force) _settings->maxConfigs = ~0U; else if ((def || _settings->preprocessOnly) && !maxconfigs) _settings->maxConfigs = 1U; if (_settings->isEnabled(Settings::UNUSED_FUNCTION) && _settings->jobs > 1) { PrintMessage("cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check."); } if (argc <= 1) { _showHelp = true; _exitAfterPrint = true; } if (_showHelp) { PrintHelp(); return true; } // Print error only if we have "real" command and expect files if (!_exitAfterPrint && _pathnames.empty() && _settings->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 (_settings->basePaths.empty() && _settings->relativePaths) _settings->basePaths = _pathnames; 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" " --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" " --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" " --doc Print a list of all available checks.\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" " --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), or compile database\n" " (compile_commands.json). 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" " * posix\n" " POSIX compatible code\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 (default)\n" " More than one --std can be used:\n" " 'cppcheck --std=c99 --std=posix file.c'\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" " --template='' 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.\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 --std=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"; } cppcheck-1.82/cli/cmdlineparser.h000066400000000000000000000057221322667425100170120ustar00rootroot00000000000000/* * 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 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 _showVersion; } /** * Return if user wanted to see list of error messages. */ bool GetShowErrorMessages() const { return _showErrorMessages; } /** * Return the path names user gave to command line. */ const std::vector& GetPathNames() const { return _pathnames; } /** * Return if help is shown to user. */ bool GetShowHelp() const { return _showHelp; } /** * Return if we should exit after printing version, help etc. */ bool ExitAfterPrinting() const { return _exitAfterPrint; } /** * Return a list of paths user wants to ignore. */ const std::vector& GetIgnoredPaths() const { return _ignoredPaths; } 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 _pathnames; std::vector _ignoredPaths; Settings *_settings; bool _showHelp; bool _showVersion; bool _showErrorMessages; bool _exitAfterPrint; }; /// @} #endif // CMDLINE_PARSER_H cppcheck-1.82/cli/cppcheckexecutor.cpp000066400000000000000000001214221322667425100200500ustar00rootroot00000000000000/* * 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 "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 #include #include // EXIT_SUCCESS and EXIT_FAILURE #include #include #include #include #include #if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__CYGWIN__) && !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::exceptionOutput = stdout; CppCheckExecutor::CppCheckExecutor() : _settings(nullptr), latestProgressOutputTime(0), errorOutput(nullptr), errorlist(false) { } CppCheckExecutor::~CppCheckExecutor() { delete errorOutput; } 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()) { errorlist = 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 { std::list::iterator iter; for (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 (std::vector::const_iterator i = ignored.cbegin(); i != ignored.cend(); ++i) { 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 PathMatch matcher(ignored, caseSensitive); for (std::vector::const_iterator iter = pathnames.begin(); iter != pathnames.end(); ++iter) FileLister::recursiveAddFiles(_files, Path::toNativeSeparators(*iter), _settings->library.markupExtensions(), matcher); } if (_files.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; CppCheck cppCheck(*this, true); const Settings& settings = cppCheck.settings(); _settings = &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); } else { return check_internal(cppCheck, argc, argv); } } void CppCheckExecutor::setSettings(const Settings &settings) { _settings = &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]= {0}; // 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 = make_container< Signalmap_t > () 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 = getpid(); #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); #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? bool unexpectedSignal=true; // unexpected indicates things didn't go as they should... 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(" - out of memory?\n", 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", (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(); BOOL result = pSymInitialize( hProcess, 0, 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++) { 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=0; } 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(); _settings = &settings; bool std = tryLoadLibrary(settings.library, argv[0], "std.cfg"); bool posix = true; if (settings.standards.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 CFGDIR const std::string details("The Cppcheck binary was compiled with CFGDIR set to \"" + std::string(CFGDIR) + "\" and will therefore search for " "std.cfg in that path."); #else const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(argv[0])) + "cfg"); const std::string details("The Cppcheck binary was compiled without CFGDIR set. Either the " "std.cfg should be available in " + cfgfolder + " or the CFGDIR " "should be configured."); #endif ErrorLogger::ErrorMessage errmsg(callstack, emptyString, Severity::information, msg+" "+details, "failedToLoadCfg", false); reportErr(errmsg); return EXIT_FAILURE; } if (settings.reportProgress) latestProgressOutputTime = std::time(nullptr); if (!settings.outputFile.empty()) { errorOutput = 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 = _files.begin(); i != _files.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 = _files.begin(); i != _files.end(); ++i) { totalfilesize += i->second; } std::size_t processedsize = 0; unsigned int c = 0; for (std::map::const_iterator i = _files.begin(); i != _files.end(); ++i) { if (!_settings->library.markupFile(i->first) || !_settings->library.processMarkupAfterCode(i->first)) { returnValue += cppcheck.check(i->first); processedsize += i->second; if (!settings.quiet) reportStatus(c + 1, _files.size(), processedsize, totalfilesize); c++; } } // filesettings c = 0; for (std::list::const_iterator fs = settings.project.fileSettings.begin(); fs != settings.project.fileSettings.end(); ++fs) { 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 = _files.begin(); i != _files.end(); ++i) { if (_settings->library.markupFile(i->first) && _settings->library.processMarkupAfterCode(i->first)) { returnValue += cppcheck.check(i->first); processedsize += i->second; if (!settings.quiet) reportStatus(c + 1, _files.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(_files, settings, *this); returnValue = executor.check(); } cppcheck.analyseWholeProgram(_settings->buildDir, _files); if (settings.isEnabled(Settings::INFORMATION) || settings.checkConfiguration) { const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled(); if (settings.jointSuppressionReport) { for (std::map::const_iterator i = _files.begin(); i != _files.end(); ++i) { reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(i->first, enableUnusedFunctionCheck)); } } reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions(enableUnusedFunctionCheck)); } 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()); } _settings = nullptr; if (returnValue) return settings.exitCode; else 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, NULL, NULL); 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 (_errorList.find(errmsg) != _errorList.end()) return; _errorList.insert(errmsg); if (errorOutput) *errorOutput << errmsg << std::endl; else { std::cerr << ansiToOEM(errmsg, (_settings == nullptr) ? true : !_settings->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 (!latestProgressOutputTime) return; // Report progress messages every 10 seconds const std::time_t currentTime = std::time(nullptr); if (currentTime >= (latestProgressOutputTime + 10)) { latestProgressOutputTime = 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 (errorlist) { reportOut(msg.toXML()); } else if (_settings->xml) { reportErr(msg.toXML()); } else { reportErr(msg.toString(_settings->verbose, _settings->outputFormat)); } } void CppCheckExecutor::setExceptionOutput(FILE* exception_output) { exceptionOutput=exception_output; } FILE* CppCheckExecutor::getExceptionOutput() { return exceptionOutput; } 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.82/cli/cppcheckexecutor.h000066400000000000000000000131531322667425100175160ustar00rootroot00000000000000/* * 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 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 */ virtual ~CppCheckExecutor(); /** * 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..." */ virtual void reportOut(const std::string &outmsg); /** xml output of errors */ virtual void reportErr(const ErrorLogger::ErrorMessage &msg); void reportProgress(const std::string &filename, const char stage[], const std::size_t value); /** * Output information messages. */ virtual void reportInfo(const ErrorLogger::ErrorMessage &msg); /** * 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* exception_output); /** * @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* _settings; /** * Used to filter out duplicate error messages. */ std::set _errorList; /** * Filename associated with size of file */ std::map _files; /** * Report progress time */ std::time_t latestProgressOutputTime; /** * Output file name for exception handler */ static FILE* exceptionOutput; /** * Error output */ std::ofstream *errorOutput; /** * Has --errorlist been given? */ bool errorlist; }; #endif // CPPCHECKEXECUTOR_H cppcheck-1.82/cli/filelister.cpp000066400000000000000000000173411322667425100166570ustar00rootroot00000000000000/* * 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 "filelister.h" #include "path.h" #include "pathmatch.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 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) { 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) { 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 { 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 (!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 (dir_result->d_type == DT_DIR || (dir_result->d_type == DT_UNKNOWN && FileLister::isDirectory(new_path))) { if (recursive && !ignored.match(new_path)) { addFiles2(files, new_path, extra, recursive, ignored); } } else { if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) { 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 (corrected_path.back() == '/') 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.82/cli/filelister.h000066400000000000000000000063201322667425100163170ustar00rootroot00000000000000/* * 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.82/cli/main.cpp000066400000000000000000000133221322667425100154340ustar00rootroot00000000000000/* * 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 . */ /** * * @mainpage Cppcheck * @version 1.82 * * @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 - Context sensitive analysis that determine possible values for each token * * Use --debug on the command line to see debug output for the token list * and the syntax tree. If both --debug and --verbose is used, the symbol * database is also written. * * The checks are written in C++. The checks are addons that can be * easily added/removed. * * @section writing_checks_sec Writing a check * Below is a simple example of a check that detect division with zero: * @code void CheckOther::checkZeroDivision() { // Iterate through all tokens in the token list for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // is this a division or modulo? if (Token::Match(tok, "[/%]")) { // try to get value '0' of rhs const ValueFlow::Value *value = tok->astOperand2()->getValue(0); // if 'value' is not NULL, rhs can be zero. if (value) reportError(tok, Severity::error, "zerodiv", "Division by zero"); } } } @endcode * * The function Token::Match is often used in the checks. Through it * you can match tokens against patterns. It is currently not possible * to write match expressions that uses the syntax tree, the symbol database, * nor the library. Only the token list is used. * * @section checkclass_sec Creating a new check class from scratch * %Check classes inherit from the Check class. The Check class specifies the interface that you must use. * To integrate a check class into cppcheck all you need to do is: * - Add your source file(s) so they are compiled into the executable. * - Create an instance of the class (the Check::Check() constructor registers the class as an addon that Cppcheck then can use). * * * @section embedding_sec Embedding Cppcheck * Cppcheck is designed to be easily embeddable into other programs. * * The "cli/main.cpp" and "cli/cppcheckexecutor.*" files illustrate how cppcheck * can be embedded into an application. * * * @section detailed_overview_sec Detailed overview * This happens when you execute cppcheck from the command line: * -# CppCheckExecutor::check this function executes the Cppcheck * -# CppCheck::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 will be removed soon." # endif #elif defined(__GNUC__) # if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4)) # warning "Using GCC 4.3 or earlier. Support for this version will be removed soon." # endif #elif defined(_MSC_VER) # if (_MSC_VER < 1600) # pragma message("WARNING: Using Visual Studio 2008 or earlier. Support for this version will be removed soon.") # endif #endif cppcheck-1.82/cli/threadexecutor.cpp000066400000000000000000000452461322667425100175500ustar00rootroot00000000000000/* * 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 "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) : _files(files), _settings(settings), _errorLogger(errorLogger), _fileCount(0) // Not initialized _fileSync, _errorSync, _reportSync { #if defined(THREADING_MODEL_FORK) _wpipe = 0; #elif defined(THREADING_MODEL_WIN) _processedFiles = 0; _totalFiles = 0; _processedSize = 0; _totalFileSize = 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) { _fileContents[ 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 << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(0); } unsigned int len = 0; if (read(rpipe, &len, sizeof(len)) <= 0) { std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::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 << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(0); } buf[readIntoBuf] = 0; if (type == REPORT_OUT) { _errorLogger.reportOut(buf); } else if (type == REPORT_ERROR || type == REPORT_INFO) { ErrorLogger::ErrorMessage msg; msg.deserialize(buf); std::string file; unsigned int line(0); if (!msg._callStack.empty()) { file = msg._callStack.back().getfile(false); line = msg._callStack.back().line; } if (!_settings.nomsg.isSuppressed(msg._id, file, line)) { // Alert only about unique errors std::string errmsg = msg.toString(_settings.verbose); if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end()) { _errorList.push_back(errmsg); if (type == REPORT_ERROR) _errorLogger.reportErr(msg); else _errorLogger.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 || !_settings.loadAverage) { return true; } double sample(0); if (getloadavg(&sample, 1) != 1) { // disable load average checking on getloadavg error return true; } else if (sample < _settings.loadAverage) { return true; } return false; #endif } unsigned int ThreadExecutor::check() { _fileCount = 0; unsigned int result = 0; std::size_t totalfilesize = 0; for (std::map::const_iterator i = _files.begin(); i != _files.end(); ++i) { totalfilesize += i->second; } std::list rpipes; std::map childFile; std::map pipeFile; std::size_t processedsize = 0; std::map::const_iterator iFile = _files.begin(); std::list::const_iterator iFileSettings = _settings.project.fileSettings.begin(); for (;;) { // Start a new child size_t nchildren = rpipes.size(); if ((iFile != _files.end() || iFileSettings != _settings.project.fileSettings.end()) && nchildren < _settings.jobs && checkLoadAverage(nchildren)) { int pipes[2]; if (pipe(pipes) == -1) { std::cerr << "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 << "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 << "fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid < 0) { // Error std::cerr << "Failed to create child process: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } else if (pid == 0) { close(pipes[0]); _wpipe = pipes[1]; CppCheck fileChecker(*this, false); fileChecker.settings() = _settings; unsigned int resultOfCheck = 0; if (iFileSettings != _settings.project.fileSettings.end()) { resultOfCheck = fileChecker.check(*iFileSettings); } else if (!_fileContents.empty() && _fileContents.find(iFile->first) != _fileContents.end()) { // File content was given as a string resultOfCheck = fileChecker.check(iFile->first, _fileContents[ 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 != _settings.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 = _files.find(name); if (fs != _files.end()) { size = fs->second; } } _fileCount++; processedsize += size; if (!_settings.quiet) CppCheckExecutor::reportStatus(_fileCount, _files.size() + _settings.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.push_back(ErrorLogger::ErrorMessage::FileLocation(childname, 0)); const ErrorLogger::ErrorMessage errmsg(locations, emptyString, Severity::error, oss.str(), "cppcheckError", false); if (!_settings.nomsg.isSuppressed(errmsg._id, childname, 0)) _errorLogger.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(_wpipe, 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) { _fileContents[path] = content; } unsigned int ThreadExecutor::check() { HANDLE *threadHandles = new HANDLE[_settings.jobs]; _itNextFile = _files.begin(); _itNextFileSettings = _settings.project.fileSettings.begin(); _processedFiles = 0; _processedSize = 0; _totalFiles = _files.size() + _settings.project.fileSettings.size(); _totalFileSize = 0; for (std::map::const_iterator i = _files.begin(); i != _files.end(); ++i) { _totalFileSize += i->second; } InitializeCriticalSection(&_fileSync); InitializeCriticalSection(&_errorSync); InitializeCriticalSection(&_reportSync); for (unsigned int i = 0; i < _settings.jobs; ++i) { threadHandles[i] = (HANDLE)_beginthreadex(nullptr, 0, threadProc, this, 0, nullptr); if (!threadHandles[i]) { std::cerr << "#### .\nThreadExecutor::check error, errno :" << errno << std::endl; exit(EXIT_FAILURE); } } DWORD waitResult = WaitForMultipleObjects(_settings.jobs, threadHandles, TRUE, INFINITE); if (waitResult != WAIT_OBJECT_0) { if (waitResult == WAIT_FAILED) { std::cerr << "#### .\nThreadExecutor::check wait failed, result: " << waitResult << " error: " << GetLastError() << std::endl; exit(EXIT_FAILURE); } else { std::cerr << "#### .\nThreadExecutor::check wait failed, result: " << waitResult << std::endl; exit(EXIT_FAILURE); } } unsigned int result = 0; for (unsigned int i = 0; i < _settings.jobs; ++i) { DWORD exitCode; if (!GetExitCodeThread(threadHandles[i], &exitCode)) { std::cerr << "#### .\nThreadExecutor::check get exit code failed, error:" << GetLastError() << std::endl; exit(EXIT_FAILURE); } result += exitCode; if (!CloseHandle(threadHandles[i])) { std::cerr << "#### .\nThreadExecutor::check close handle failed, error:" << GetLastError() << std::endl; exit(EXIT_FAILURE); } } DeleteCriticalSection(&_fileSync); DeleteCriticalSection(&_errorSync); DeleteCriticalSection(&_reportSync); 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->_itNextFile; std::list::const_iterator &itFileSettings = threadExecutor->_itNextFileSettings; // guard static members of CppCheck against concurrent access EnterCriticalSection(&threadExecutor->_fileSync); CppCheck fileChecker(*threadExecutor, false); fileChecker.settings() = threadExecutor->_settings; for (;;) { if (itFile == threadExecutor->_files.end() && itFileSettings == threadExecutor->_settings.project.fileSettings.end()) { LeaveCriticalSection(&threadExecutor->_fileSync); break; } std::size_t fileSize = 0; if (itFile != threadExecutor->_files.end()) { const std::string &file = itFile->first; fileSize = itFile->second; ++itFile; LeaveCriticalSection(&threadExecutor->_fileSync); std::map::const_iterator fileContent = threadExecutor->_fileContents.find(file); if (fileContent != threadExecutor->_fileContents.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->_fileSync); result += fileChecker.check(fs); } EnterCriticalSection(&threadExecutor->_fileSync); threadExecutor->_processedSize += fileSize; threadExecutor->_processedFiles++; if (!threadExecutor->_settings.quiet) { EnterCriticalSection(&threadExecutor->_reportSync); CppCheckExecutor::reportStatus(threadExecutor->_processedFiles, threadExecutor->_totalFiles, threadExecutor->_processedSize, threadExecutor->_totalFileSize); LeaveCriticalSection(&threadExecutor->_reportSync); } } return result; } void ThreadExecutor::reportOut(const std::string &outmsg) { EnterCriticalSection(&_reportSync); _errorLogger.reportOut(outmsg); LeaveCriticalSection(&_reportSync); } void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &msg) { report(msg, REPORT_ERROR); } void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg) { report(msg, REPORT_INFO); } void ThreadExecutor::report(const ErrorLogger::ErrorMessage &msg, MessageType msgType) { std::string file; unsigned int line(0); if (!msg._callStack.empty()) { file = msg._callStack.back().getfile(false); line = msg._callStack.back().line; } if (_settings.nomsg.isSuppressed(msg._id, file, line)) return; // Alert only about unique errors bool reportError = false; std::string errmsg = msg.toString(_settings.verbose); EnterCriticalSection(&_errorSync); if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end()) { _errorList.push_back(errmsg); reportError = true; } LeaveCriticalSection(&_errorSync); if (reportError) { EnterCriticalSection(&_reportSync); switch (msgType) { case REPORT_ERROR: _errorLogger.reportErr(msg); break; case REPORT_INFO: _errorLogger.reportInfo(msg); break; } LeaveCriticalSection(&_reportSync); } } #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.82/cli/threadexecutor.h000066400000000000000000000105401322667425100172020ustar00rootroot00000000000000/* * 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 THREADEXECUTOR_H #define THREADEXECUTOR_H #include "errorlogger.h" #include "importproject.h" #include #include #include #include #if (defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__) #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); virtual ~ThreadExecutor(); unsigned int check(); virtual void reportOut(const std::string &outmsg); virtual void reportErr(const ErrorLogger::ErrorMessage &msg); virtual void reportInfo(const ErrorLogger::ErrorMessage &msg); /** * @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 &_files; Settings &_settings; ErrorLogger &_errorLogger; unsigned int _fileCount; #if defined(THREADING_MODEL_FORK) /** @brief Key is file name, and value is the content of the file */ std::map _fileContents; 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 _errorList; int _wpipe; /** * @brief Check load average condition * @param nchildren - count of currently runned 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 MessageType {REPORT_ERROR, REPORT_INFO}; std::map _fileContents; std::map::const_iterator _itNextFile; std::list::const_iterator _itNextFileSettings; std::size_t _processedFiles; std::size_t _totalFiles; std::size_t _processedSize; std::size_t _totalFileSize; CRITICAL_SECTION _fileSync; std::list _errorList; CRITICAL_SECTION _errorSync; CRITICAL_SECTION _reportSync; 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 private: /** disabled copy constructor */ ThreadExecutor(const ThreadExecutor &); /** disabled assignment operator */ void operator=(const ThreadExecutor &); }; /// @} #endif // THREADEXECUTOR_H cppcheck-1.82/cli/version.rc000066400000000000000000000016441322667425100160230ustar00rootroot00000000000000#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.82/cmake/000077500000000000000000000000001322667425100143145ustar00rootroot00000000000000cppcheck-1.82/cmake/buildFiles.cmake000066400000000000000000000003461322667425100174030ustar00rootroot00000000000000CONFIGURE_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.82/cmake/cmake_uninstall.cmake.in000066400000000000000000000012651322667425100211000ustar00rootroot00000000000000# ----------------------------------------------- # 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.82/cmake/compilerDefinitions.cmake000066400000000000000000000004321322667425100213230ustar00rootroot00000000000000if (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(-DCFGDIR="${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}") endif() cppcheck-1.82/cmake/compileroptions.cmake000066400000000000000000000133461322667425100205530ustar00rootroot00000000000000set(EXTRA_C_FLAGS "") set(EXTRA_C_FLAGS_RELEASE "-DNDEBUG") set(EXTRA_C_FLAGS_DEBUG "-DDEBUG -O0") 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} -Wabi") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wcast-qual") # Cast for removing type qualifiers set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wconversion") # 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} -Wshadow") # whenever a local variable or type declaration shadows another one set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wsign-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() 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 "${EXTRA_C_FLAGS} -Wextra -pedantic") # 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 "${EXTRA_C_FLAGS} -Wall -std=c++0x") 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() 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.82/cmake/dynamic_analyzer_options.cmake000066400000000000000000000016661322667425100224330ustar00rootroot00000000000000IF (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.82/cmake/findDependencies.cmake000066400000000000000000000011671322667425100205520ustar00rootroot00000000000000set(GUI_QT_COMPONENTS Core Gui Widgets PrintSupport) find_package(Qt5 COMPONENTS ${GUI_QT_COMPONENTS}) find_package(Qt5LinguistTools) 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.82/cmake/options.cmake000066400000000000000000000050361322667425100170150ustar00rootroot00000000000000#------------------------------------------------------ # 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 compliler") 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(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) cppcheck-1.82/cmake/printInfo.cmake000066400000000000000000000051061322667425100172700ustar00rootroot00000000000000message( 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 ) message( STATUS "BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}" ) message( STATUS "BUILD_TESTS = ${BUILD_TESTS}" ) message( STATUS "BUILD_GUI = ${BUILD_GUI}" ) 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.82/cmake/versions.cmake000066400000000000000000000005221322667425100171650ustar00rootroot00000000000000# Version for libraries CPP SET(VERSION "1.82") 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.82/console_common.pri000066400000000000000000000010731322667425100167630ustar00rootroot00000000000000# 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.82/cppcheck.cbp000066400000000000000000000212521322667425100155040ustar00rootroot00000000000000 cppcheck-1.82/cppcheck.cppcheck000066400000000000000000000007731322667425100165250ustar00rootroot00000000000000 cppcheck-1.82/cppcheck.sln000066400000000000000000000123361322667425100155370ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.25914.0 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.82/createrelease000077500000000000000000000056561322667425100160020ustar00rootroot00000000000000#!/bin/bash # # A script for creating release packages. The release packages are create in the home directory. # # self check: # ./cppcheck --library=cppcheck-lib --enable=style --inconclusive --suppress=bitwiseOnBoolean cli gui/*.cpp lib # # Update translations # lupdate gui.pro # # Update copyright year # git diff c4caee6b1828e3688c88ebacfc8ac90eace8a7ac | grep '^diff --git a/' | sed 's|.* b/||' | xargs sed -i 's/Copyright (C) 2007-201./Copyright (C) 2007-2018/' # git diff | grep '^diff --git a/' # # Make sure "cppcheck --errorlist" works. For example with: # make test # ./cppcheck --errorlist > errlist.xml && xmllint --noout errlist.xml # ./cppcheck --xml-version=2 --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.82/" cli/main.cpp # sed -i "s/1.[0-9][0-9].99/1.82/" cmake/versions.cmake # sed -i "s/MINOR [0-9][0-9]/MINOR 82/" lib/version.h # sed -i "s/1.[0-9][0-9] dev/1.82/" man/manual.docbook # sed -i "s/1.[0-9][0-9] dev/1.82/" win_installer/productInfo.wxi # sed -i "s/1.[0-9][0-9].99/1.82/" win_installer/productInfo.wxi # 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 release: # ./createrelease 1.43 # # Restore the Makefile: # ./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 # Tag to use tag=$1 # Name of release releasename=cppcheck-$tag # Create archives.. git archive --format=tar --prefix=$releasename/ $tag | gzip > ~/$releasename.tar.gz git archive --format=tar --prefix=$releasename/ $tag | bzip2 > ~/$releasename.tar.bz2 git archive --format=zip -9 --prefix=$releasename/ $tag > ~/$releasename.zip scp ~/$releasename.* danielmarjamaki,cppcheck@frs.sourceforge.net:/home/frs/project/c/cp/cppcheck/cppcheck/$tag/ # Generate the manual.pdf, manual.html and version.txt make clean && make ./cppcheck --version > version.txt scp version.txt danielmarjamaki,cppcheck@web.sourceforge.net:htdocs/ xsltproc -xinclude -o manual.fo /usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl man/manual.docbook fop manual.fo -pdf manual.pdf #docbook2pdf man/manual.docbook scp manual.pdf danielmarjamaki,cppcheck@web.sourceforge.net:htdocs/ xsltproc -o manual.html /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl man/manual.docbook scp manual.html danielmarjamaki,cppcheck@web.sourceforge.net:htdocs/ cppcheck-1.82/democlient/000077500000000000000000000000001322667425100153575ustar00rootroot00000000000000cppcheck-1.82/democlient/build.sh000077500000000000000000000013101322667425100170100ustar00rootroot00000000000000#!/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.82/democlient/democlient.cpp000066400000000000000000000054551322667425100202170ustar00rootroot00000000000000#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) { } void reportErr(const ErrorLogger::ErrorMessage &msg) { 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 unsigned int value) { 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.82/doxyfile000066400000000000000000002365141322667425100150150ustar00rootroot00000000000000# 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 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 = 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 acurate 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 therefor 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.82/externals/000077500000000000000000000000001322667425100152415ustar00rootroot00000000000000cppcheck-1.82/externals/externals.pri000066400000000000000000000003621322667425100177630ustar00rootroot00000000000000INCLUDEPATH += $${PWD}/simplecpp \ $${PWD}/tinyxml HEADERS += $${PWD}/simplecpp/simplecpp.h \ $${PWD}/tinyxml/tinyxml2.h SOURCES += $${PWD}/simplecpp/simplecpp.cpp \ $${PWD}/tinyxml/tinyxml2.cpp cppcheck-1.82/externals/simplecpp/000077500000000000000000000000001322667425100172355ustar00rootroot00000000000000cppcheck-1.82/externals/simplecpp/CMakeLists.txt000066400000000000000000000001441322667425100217740ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_library(simplecpp_objs OBJECT ${srcs} ${hdrs}) cppcheck-1.82/externals/simplecpp/LICENSE000066400000000000000000000167441322667425100202560ustar00rootroot00000000000000 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.82/externals/simplecpp/simplecpp.cpp000066400000000000000000002734641322667425100217550ustar00rootroot00000000000000/* * 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 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); std::istringstream istr(hex ? s.substr(2) : s); if (hex) istr >> std::hex; istr >> ret; return ret; } static unsigned long long stringToULL(const std::string &s) { unsigned long long ret; const bool hex = isHex(s); std::istringstream istr(hex ? s.substr(2) : s); if (hex) istr >> std::hex; 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))); } 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; } 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; } 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 unsigned char 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(); 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 isRawStringId(const std::string &str) { return str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; } 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%") { loc.push(location); location.line = std::atol(cback()->str.c_str()); } else if (lastline == "# line %num% %str%") { loc.push(location); location.fileIndex = fileIndex(cback()->str.substr(1U, cback()->str.size() - 2U)); location.line = std::atol(cback()->previous->str.c_str()); } // #endfile else if (lastline == "# endfile" && !loc.empty()) { location = loc.top(); loc.pop(); } } continue; } if (std::isspace(ch)) { location.col++; continue; } TokenString currentToken; // 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 { istr.unget(); } } // 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 == '\'') { // C++11 raw string literal if (ch == '\"' && cback() && cback()->name && isRawStringId(cback()->str)) { std::string delim; ch = readChar(istr,bom); while (istr.good() && ch != '(' && ch != '\n') { delim += ch; ch = readChar(istr,bom); } if (!istr.good() || ch == '\n') // TODO report return; currentToken = '\"'; const std::string endOfRawString(')' + delim + '\"'); while (istr.good() && !endsWith(currentToken, endOfRawString)) currentToken += readChar(istr,bom); if (!endsWith(currentToken, endOfRawString)) // TODO report return; currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U); if (cback()->op == 'R') back()->setstr(escapeString(currentToken)); else { back()->setstr(cback()->str.substr(0, cback()->str.size() - 1)); push_back(new Token(currentToken, location)); // push string without newlines } 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); if (currentToken.size() < 2U) // TODO report return; std::string s = currentToken; std::string::size_type pos; while ((pos = s.find_first_of("\r\n")) != std::string::npos) { s.erase(pos,1); } push_back(new Token(s, location)); // push string without newlines location.adjust(currentToken); continue; } else { currentToken += ch; } if (currentToken == "<" && lastLine() == "# include") { currentToken = readUntil(istr, location, '<', '>', outputList); 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() { for (Token *tok = front(); tok; tok = tok->next) { if (tok->op == '.') { if (tok->previous && tok->previous->op == '.') continue; if (tok->next && tok->next->op == '.') 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("Ee"))) { 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 && !isHex(tok->str) && (lastChar == 'E' || lastChar == 'e') && 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("=!<>+-*/%&|^")) { 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->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) { std::string ret; ret += start; bool backslash = false; char ch = 0; while (ch != end && ch != '\r' && ch != '\n' && istr.good()) { ch = (unsigned char)istr.get(); if (backslash && ch == '\n') { ch = 0; backslash = false; continue; } backslash = false; ret += ch; if (ch == '\\') { const char next = (unsigned char)istr.get(); if (next == '\r' || next == '\n') { ret.erase(ret.size()-1U); backslash = (next == '\r'); } ret += 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 = ' ' + ret; ret = (tok->str[0] == '\"' ? std::string("%str%") : std::isdigit(static_cast(tok->str[0])) ? std::string("%num%") : tok->str) + ret; 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) {} Macro(const Token *tok, std::vector &f) : nameTokDef(NULL), files(f), tokenListDefine(f) { 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) { const std::string def(name + ' ' + value); std::istringstream istr(def); tokenListDefine.readfile(istr); if (!parseDefine(tokenListDefine.cfront())) throw std::runtime_error("bad macro syntax"); } Macro(const Macro ¯o) : nameTokDef(NULL), files(macro.files), tokenListDefine(macro.files) { *this = macro; } void operator=(const Macro ¯o) { if (this != ¯o) { if (macro.tokenListDefine.empty()) parseDefine(macro.nameTokDef); else { tokenListDefine = macro.tokenListDefine; parseDefine(tokenListDefine.cfront()); } } } /** * 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; 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->op == '.' && argtok->next && argtok->next->op == '.' && argtok->next->next && argtok->next->next->op == '.' && argtok->next->next->next && argtok->next->next->next->op == ')') { variadic = true; if (!argtok->previous->name) args.push_back("__VA_ARGS__"); argtok = argtok->next->next->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, macros, files); 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 (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; } 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()); Token *B = tok->next->next; 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; }; } #ifdef SIMPLECPP_WINDOWS 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 WIN32_FIND_DATAA FindFileData; HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); if (INVALID_HANDLE_VALUE == hFind) return false; *result = FindFileData.cFileName; FindClose(hFind); return true; } /** 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 // 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; } // Append real filename (proper case) std::string f2; if (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; } 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 realFilename(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; } static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader) { if (isAbsolutePath(header)) { f.open(header.c_str()); return f.is_open() ? simplecpp::simplifyPath(header) : ""; } if (!systemheader) { if (sourcefile.find_first_of("\\/") != std::string::npos) { const std::string s = sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header; f.open(s.c_str()); if (f.is_open()) return simplecpp::simplifyPath(s); } else { f.open(header.c_str()); if (f.is_open()) return simplecpp::simplifyPath(header); } } 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; f.open(s.c_str()); if (f.is_open()) return simplecpp::simplifyPath(s); } 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()) 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::pair(std::string("char"), sizeof(char))); sizeOfType.insert(std::pair(std::string("short"), sizeof(short))); sizeOfType.insert(std::pair(std::string("short int"), sizeOfType["short"])); sizeOfType.insert(std::pair(std::string("int"), sizeof(int))); sizeOfType.insert(std::pair(std::string("long"), sizeof(long))); sizeOfType.insert(std::pair(std::string("long int"), sizeOfType["long"])); sizeOfType.insert(std::pair(std::string("long long"), sizeof(long long))); sizeOfType.insert(std::pair(std::string("float"), sizeof(float))); sizeOfType.insert(std::pair(std::string("double"), sizeof(double))); sizeOfType.insert(std::pair(std::string("long double"), sizeof(long double))); sizeOfType.insert(std::pair(std::string("char *"), sizeof(char *))); sizeOfType.insert(std::pair(std::string("short *"), sizeof(short *))); sizeOfType.insert(std::pair(std::string("short int *"), sizeOfType["short *"])); sizeOfType.insert(std::pair(std::string("int *"), sizeof(int *))); sizeOfType.insert(std::pair(std::string("long *"), sizeof(long *))); sizeOfType.insert(std::pair(std::string("long int *"), sizeOfType["long *"])); sizeOfType.insert(std::pair(std::string("long long *"), sizeof(long long *))); sizeOfType.insert(std::pair(std::string("float *"), sizeof(float *))); sizeOfType.insert(std::pair(std::string("double *"), sizeof(double *))); sizeOfType.insert(std::pair(std::string("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::pair("__FILE__", Macro("__FILE__", "__FILE__", files))); macros.insert(std::pair("__LINE__", Macro("__LINE__", "__LINE__", files))); macros.insert(std::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)) { rawtok = rawtok->next; if (!rawtok || !rawtok->name) { if (rawtok) 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()->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: " + rawtok->next->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() : 0; 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; } } 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); 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.82/externals/simplecpp/simplecpp.h000066400000000000000000000230011322667425100213760ustar00rootroot00000000000000/* * 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: 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) : str(string), location(loc), previous(NULL), next(NULL), string(s) { flags(); } Token(const Token &tok) : str(string), macro(tok.macro), location(tok.location), previous(NULL), next(NULL), string(tok.str) { flags(); } void flags() { name = (std::isalpha((unsigned char)str[0]) || str[0] == '_' || str[0] == '$'); comment = (str.compare(0, 2, "//") == 0 || str.compare(0, 2, "/*") == 0); number = std::isdigit((unsigned char)str[0]) || (str.size() > 1U && str[0] == '-' && std::isdigit((unsigned char)str[1])); op = (str.size() == 1U) ? str[0] : '\0'; } 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; const TokenString &str; 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 } 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 = 0); TokenList(const TokenList &other); ~TokenList(); TokenList &operator=(const TokenList &other); 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 = 0); 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, const char start, const char end, OutputList *outputList); std::string lastLine(int maxsize=10) 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) : macroLocation(f), useLocation(f) {} std::string macroName; Location macroLocation; Location useLocation; }; /** * 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 = 0); /** * 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 = 0, std::list *macroUsage = 0); /** * Deallocate data */ SIMPLECPP_LIB void cleanup(std::map &filedata); /** Simplify path */ SIMPLECPP_LIB std::string simplifyPath(std::string path); } #endif cppcheck-1.82/externals/tinyxml/000077500000000000000000000000001322667425100167455ustar00rootroot00000000000000cppcheck-1.82/externals/tinyxml/CMakeLists.txt000066400000000000000000000001421322667425100215020ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_library(tinyxml_objs OBJECT ${srcs} ${hdrs}) cppcheck-1.82/externals/tinyxml/tinyxml2.cpp000066400000000000000000002157611322667425100212530ustar00rootroot00000000000000/* 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 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 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 ) { Clear(); FILE* fp = callfopen( filename, "rb" ); if ( !fp ) { SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ? 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 ) { FILE* fp = callfopen( filename, "w" ); if ( !fp ) { SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ? 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.82/externals/tinyxml/tinyxml2.h000066400000000000000000002106761322667425100207200ustar00rootroot00000000000000/* 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 -DDEBUG 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 DEBUG # define 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(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 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 occured, 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.82/generate_coverage_report000077500000000000000000000010211322667425100202140ustar00rootroot00000000000000#!/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.82/gui/000077500000000000000000000000001322667425100140205ustar00rootroot00000000000000cppcheck-1.82/gui/CMakeLists.txt000066400000000000000000000027071322667425100165660ustar00rootroot00000000000000if (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() qt5_use_modules(cppcheck-gui ${GUI_QT_COMPONENTS}) 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.82/gui/about.ui000066400000000000000000000101711322667425100154710ustar00rootroot00000000000000 About 0 0 478 300 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-2018 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 Qt::Horizontal QDialogButtonBox::Ok mButtons accepted() About accept() 248 254 157 274 mButtons rejected() About reject() 316 260 286 274 cppcheck-1.82/gui/aboutdialog.cpp000066400000000000000000000025561322667425100170260ustar00rootroot00000000000000/* * 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.82/gui/aboutdialog.h000066400000000000000000000022131322667425100164610ustar00rootroot00000000000000/* * 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 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 = 0); private: Ui::About mUI; }; /// @} #endif // ABOUT_DIALOG_H cppcheck-1.82/gui/application.cpp000066400000000000000000000016761322667425100170410ustar00rootroot00000000000000/* * 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 "application.h" Application::Application(const QString &name, const QString &path, const QString ¶ms) : mName(name) , mPath(path) , mParameters(params) { } cppcheck-1.82/gui/application.h000066400000000000000000000056761322667425100165120ustar00rootroot00000000000000/* * 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.82/gui/application.ui000066400000000000000000000125211322667425100166630ustar00rootroot00000000000000 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.82/gui/applicationdialog.cpp000066400000000000000000000057761322667425100202260ustar00rootroot00000000000000/* * 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.82/gui/applicationdialog.h000066400000000000000000000034701322667425100176600ustar00rootroot00000000000000/* * 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 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 = 0); 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.82/gui/applicationlist.cpp000066400000000000000000000162741322667425100177350ustar00rootroot00000000000000/* * 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 "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; } return foundOne; } cppcheck-1.82/gui/applicationlist.h000066400000000000000000000064231322667425100173750ustar00rootroot00000000000000/* * 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 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 = 0); 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.82/gui/checkstatistics.cpp000066400000000000000000000061761322667425100177260ustar00rootroot00000000000000/* * 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.82/gui/checkstatistics.h000066400000000000000000000036101322667425100173610ustar00rootroot00000000000000/* * 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 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 = NULL); /** * @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.82/gui/checkthread.cpp000066400000000000000000000433121322667425100167740ustar00rootroot00000000000000/* * 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 #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 I = fileSettings->includePaths.begin(); I != fileSettings->includePaths.end(); ++I) args << ("-I" + QString::fromStdString(*I)); for (std::list::const_iterator i = fileSettings->systemIncludePaths.begin(); i != fileSettings->systemIncludePaths.end(); ++i) args << "-isystem" << QString::fromStdString(*i); foreach (QString D, QString::fromStdString(fileSettings->defines).split(";")) { args << ("-D" + D); } 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; }; } 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 << dumpFile; 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); process.waitForFinished(); const QString errout(process.readAllStandardError()); QFile f(dumpFile + '-' + addon + "-results"); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); out << errout; f.close(); } parseAddonErrors(errout, addon); } } } void CheckThread::stop() { mState = Stopping; mCppcheck.terminate(); } void CheckThread::parseAddonErrors(QString err, QString tool) { Q_UNUSED(tool); QTextStream in(&err, QIODevice::ReadOnly); while (!in.atEnd()) { QString line = in.readLine(); QRegExp r1("\\[([a-zA-Z]?:?[^:]+):([0-9]+)\\][ ][(]([a-z]+)[)]: (.+) \\[([a-zA-Z0-9_\\-\\.]+)\\]"); if (!r1.exactMatch(line)) continue; const std::string &filename = r1.cap(1).toStdString(); const int lineNumber = r1.cap(2).toInt(); const std::string severity = r1.cap(3).toStdString(); const std::string message = r1.cap(4).toStdString(); const std::string id = r1.cap(5).toStdString(); std::list callstack; callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber)); ErrorLogger::ErrorMessage errmsg(callstack, filename, Severity::fromString(severity), message, id, false); 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().col = 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().col = 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; if (mSuppressions.contains(e.errorId)) continue; std::list callstack; foreach (const QErrorPathItem &path, e.errorPath) { callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(path.file.toStdString(), path.info.toStdString(), path.line)); } 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); } } 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) { if (QFileInfo(dataDir + p + addonFile).exists()) return dataDir + p + addonFile; } } const QString appPath = QApplication::applicationDirPath(); foreach (const QString p, paths) { if (QFileInfo(dataDir + p + addonFile).exists()) return appPath + p + addonFile; } return QString(); } cppcheck-1.82/gui/checkthread.h000066400000000000000000000074371322667425100164510ustar00rootroot00000000000000/* * 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 CHECKTHREAD_H #define CHECKTHREAD_H #include #include "cppcheck.h" #include "threadresult.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 setDataDir(const QString &dataDir) { mDataDir = dataDir; } void setClangIncludePaths(const QStringList &s) { mClangIncludePaths = s; } void setSuppressions(const QStringList 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, QString tool); void parseClangErrors(const QString &tool, const QString &file0, QString err); QStringList mFiles; bool mAnalyseWholeProgram; QStringList mAddonsAndTools; QString mDataDir; QStringList mClangIncludePaths; QStringList mSuppressions; }; /// @} #endif // CHECKTHREAD_H cppcheck-1.82/gui/common.cpp000066400000000000000000000026471322667425100160250ustar00rootroot00000000000000/* * 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 "common.h" #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); } cppcheck-1.82/gui/common.h000066400000000000000000000117521322667425100154670ustar00rootroot00000000000000/* * 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 COMMON_H #define COMMON_H #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_CPP03 "Platform CPP03" #define SETTINGS_STD_CPP11 "Platform CPP11" #define SETTINGS_STD_CPP14 "Platform CPP14" #define SETTINGS_STD_C89 "Platform C89" #define SETTINGS_STD_C99 "Platform C99" #define SETTINGS_STD_C11 "Platform C11" #define SETTINGS_STD_POSIX "Platform Posix" // 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_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" /** * @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 fo 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); /// @} #endif cppcheck-1.82/gui/cppcheck-gui.desktop000066400000000000000000000002611322667425100177540ustar00rootroot00000000000000[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.82/gui/cppcheck-gui.png000066400000000000000000000047571322667425100171050ustar00rootroot00000000000000PNG  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.82/gui/cppcheck-gui.rc000066400000000000000000000002661322667425100167140ustar00rootroot00000000000000// Include command line program's resource file containing version info #include "../cli/version.rc" // GUI Icon IDI_ICON1 ICON DISCARDABLE "cppcheck.ico" cppcheck-1.82/gui/cppcheck-gui.svg000066400000000000000000000077731322667425100171210ustar00rootroot00000000000000 image/svg+xml c ++ cppcheck-1.82/gui/cppcheck.ico000066400000000000000000000611761322667425100163070ustar00rootroot00000000000000 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.82/gui/cppcheck_de.ts000066400000000000000000002563521322667425100166350ustar00rootroot00000000000000 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-2018 Cppcheck team. Copyright © 2007-2018 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 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 Can not open file %1. Datei %1 kann nicht geöffnet werden. Failed to load %1. %2. %1 kann nicht geladen werden. %2. Can not 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 constant Konstant 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&yseieren 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 &Contents &Inhalte Categories Kategorien Show style warnings Zeige Stilwarnungen Open the help contents Öffnet die Hilfe-Inhalte F1 F1 &Help &Hilfe Quick Filter: Schnellfilter: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) C/C++-Sourcecode, Compile-Datenbank, Visual Studio (%1 %2 *.sln *.vcxproj) 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! 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? 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 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 Project Projekt Paths and Defines Pfade und Definitionen Import Project (Visual studio / compile database) Importiere Projekt (Visual Studio / Compile-Datenbank) Defines must be separated by a semicolon ';' Definitionen müssen mit einem Semikolon getrennt werden. 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. ... ... <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> Analyze all Visual Studio configurations Alle Visual-Studio-Konfigurationen analysieren Paths: Pfade: Add... Hinzufügen... Edit Bearbeiten Remove Entfernen Include Paths: Includepfade: Up Auf Down Ab Root path: Wurzelverzeichnis: Warning tags (separated by semicolon) Warnungs-Tags (Semikolon-getrennt) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck-Arbeitsverzeichnis (Vollständige Programmanalyse, inkrementelle Analyse, Statistiken, etc.) Libraries Bibliotheken Exclude Ausschließen Suppressions Fehlerunterdrückungen Suppression list: Fehlerunterdrückungsliste: Add Hinzufügen Addons Add-Ons Y2038 Y2038 Thread safety Threadsicherheit Coding standards Programmierstandards Cert Cert Extra Tools Zusatzwerkzeuge It is common best practice to use several tools. Es wird empfohlen, mehrere Werkzeuge einzusetzen. Clang analyzer Clang-Analyzer Clang-tidy Clang-Tidy Defines: Definitionen: ProjectFileDialog Project file: %1 Projektdatei: %1 Select Cppcheck build dir Wähle Cppcheck-Erstellungsverzeichnis Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Visual Studio (*.sln *.vcxproj);;Compile-Datenbank (compile_commands.json) Select include directory Wähle Include-Verzeichnisse Select a directory to check Wähle zu prüfendes Verzeichnis Clang-tidy (not found) Clang-tidy (nicht gefunden) Import Project Projekt importieren Select directory to ignore Wähle zu ignorierendes Verzeichnis Add Suppression Fehlerunterdrückung hinzufügen Select error id suppress: Wähle Fehler-ID zum Unterdrücken: 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) QPlatformTheme OK OK Cancel Abbrechen Close Schließen Save Speichern ResultsTree File Datei Severity Schweregrad Line Zeile Summary Zusammenfassung Undefined file Undefinierte Datei [Inconclusive] [unklar] debug Debug note Anmerkung Recheck Erneut prüfen Copy filename Dateiname kopieren Copy full path Vollständigen Pfad kopieren Copy message Meldung kopieren Copy message id Meldungs-Id kopieren 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. Could not find file: %1 Please select the directory where file is located. Datei konnte nicht gefunden werden: %1 Bitte wählen Sie das Verzeichnis, in dem sich die Datei befindet. 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. Summary Zusammenfassung Message Meldung 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 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) ... ... 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> 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 clang path Clang-Verzeichnis auswählen StatsDialog Statistics Statistik Project Projekt Project: Projekt: Paths: Pfade: Include paths: Include-Pfade: Defines: 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 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 cppcheck-1.82/gui/cppcheck_es.ts000066400000000000000000002667711322667425100166620ustar00rootroot00000000000000 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-2018 Cppcheck team. Copyright © 2007-2018 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 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 Can not open file %1. Failed to load %1. %2. 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 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! C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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! 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? 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 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) 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. ... <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: Rutas: Add... Añadir... Edit Editar Remove Eliminar Include Paths: Includes Incluir Include directories: Incluir los directorios: Up Subir Down Bajar 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 Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: Definiciones: ProjectFileDialog Project file: %1 Archivo de proyecto: %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Selecciona una carpeta para incluir Select a directory to check Selecciona la carpeta a comprobar Clang-tidy (not found) Import Project Select directory to ignore Selecciona la carpeta a ignorar Add Suppression Añadir supresión Select error id suppress: 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) QPlatformTheme OK Aceptar Cancel Cancelar Close Cerrar Save Guardar ResultsTree File Archivo Severity Severidad Line Línea Summary Resumen Undefined file Fichero no definido [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 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) ... 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> 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 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: 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 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 cppcheck-1.82/gui/cppcheck_fi.ts000066400000000000000000002525721322667425100166430ustar00rootroot00000000000000 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-2018 Cppcheck team. Copyright (C) 2007-2018 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 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 Can not open file %1. Failed to load %1. %2. 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 &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: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 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! 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? 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 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 Project Paths and Defines Import Project (Visual studio / compile database) 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. ... <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: Add... Edit Remove Include Paths: Up Down Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Suppressions Suppression list: Add Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: ProjectFileDialog Project file: %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Select a directory to check Clang-tidy (not found) Import Project Select directory to ignore Add Suppression Select error id suppress: 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) QPlatformTheme OK Cancel Close Save ResultsTree File Tiedosto Severity Tyyppi Line Rivi Summary Undefined file Määrittelemätön tiedosto [Inconclusive] debug note Recheck Copy filename Kopioi tiedostonimi Copy full path Kopioi tiedoston koko polku Copy message Copy message 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. 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. Could not find file: %1 Please select the directory where file is located. 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. Summary Message 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 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) ... 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> 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 clang path StatsDialog Statistics Project Project: Paths: Include paths: Defines: 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 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 cppcheck-1.82/gui/cppcheck_fr.ts000066400000000000000000001711511322667425100166450ustar00rootroot00000000000000 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-2018 Cppcheck team. 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: &Parameters: 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: You must specify a name, a path and optionally parameters for the 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 Function name(s) Number of 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 Can not open file %1. Can not save file %1. Save the library as Failed to load %1. %2. 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 C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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) 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. Defines must be separated by a semicolon ';' ... Include Paths: Paths and Defines Import Project (Visual studio / compile database) <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 Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy 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 Add Suppression Select error id suppress: Select Cppcheck build dir Import Project Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Clang-tidy (not found) 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) 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 [Inconclusive] 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 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 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> 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 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 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 cppcheck-1.82/gui/cppcheck_it.ts000066400000000000000000002706121322667425100166540ustar00rootroot00000000000000 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-2018 Cppcheck team. Copyright © 2007-2018 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 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 Can not open file %1. Failed to load %1. %2. 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 &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: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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! 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? 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 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) 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. ... <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: Percorsi: Add... Aggiungi... Edit Modifica Remove Rimuovi Include Paths: Includes Inclusioni Include directories: Cartelle di inclusione: Up Su Down Giù Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Escludi Suppressions Suppression list: Add Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: Definizioni: ProjectFileDialog Project file: %1 File di progetto: %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Seleziona la cartella da includere Select a directory to check Seleziona una cartella da scansionare Clang-tidy (not found) Import Project Select directory to ignore Seleziona la cartella da ignorare Add Suppression Select error id suppress: 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) QPlatformTheme OK Cancel Close Chiudi Save ResultsTree File File Severity Severità Line Linea Summary Riassunto Undefined file File indefinito [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 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) ... 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> 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 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: 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 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 cppcheck-1.82/gui/cppcheck_ja.ts000066400000000000000000003014141322667425100166250ustar00rootroot00000000000000 About About Cppcheck cppcheckについて Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. CppcheckはC/C++ 静的コード解析ツールです. Copyright © 2007-2018 Cppcheck team. Copyright © 2007-2017 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 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) ここにエラー指摘のあるファイルを開くことのできるアプリケーションを追加できます。そのアプリケーション(Executable:)は実行可能なのもので、適切なコマンドラインパラメータを処理できるものにしてください。 パラメータ中の以下の文字列を使用してパラメータ(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 Can not open file %1. Failed to load %1. %2. 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> <html><head/><body> <p>例えば、比較からの返り値または '!' 演算子からの返り値等でbool値は許可されていますか?</p> <p>典型的には、引数がポインタやサイズの場合にこれをセットします。</p> <p>例:</p> <pre> memcmp(x, y, i == 123); // 最後の引数はbool値を許可しない </pre> </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 標準(&S) Categories カテゴリ(&C) &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 Clang Show Clang results &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 Filter フィルター &Reanalyze modified files &Recheck modified files 変更のあったファイルを再チェック(&R) 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 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. エディタアプリの設定の読み込みで問題が発生しました。 2つの理由が考えられます。 1. 古いバージョンのCppCheckの設定には互換性のないものがあります。 2. 指定のエディタアプリケーションが正しく起動していない可能性があります。 エディタアプリの設定を確認し修正してください。 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) 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' には不明要素が含まれています。 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' 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. 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! 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? 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. シンタックス: cppcheck-gui [OPTIONS] [files または paths] オプション: -h, --help このヘルプを表示する。 -p <file> 指定のプロジェクトファイルを開き、チェックを開始する。 -l <file> 指定の、結果XMLファイルを開く -d <directory> フォルダを指定してチェックする。これは -l オプションで 指定した、結果XMLファイルを生成する。 -v, --version バージョンを表示する。 --data-dir=<directory> GUI のデータファイルのあるディレクトリを指定する。(翻訳やcfg) Cppcheck GUI - Command line parameters Cppcheck GUI - コマンドラインパラメータ 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) Defines must be separated by a semicolon ';' 定義(Define)はセミコロン';'で区切る必要があります。 &Root: Root: ルート: Libraries: ライブラリ Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. カスタマイズした cfgファイルを同じフォルダにプロジェクトファイルとして保存してください。ここに表示できるようになります。 ... <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: パス: Add... 追加... Edit 編集 Remove 取り除く Include Paths: Includes インクルード Include directories: インクルードディレクトリ: Up Down Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude 除外する Suppressions 指摘の抑制 Suppression list: 抑制リスト Add 追加 Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: 定義(Defines): ProjectFileDialog Project file: %1 プロジェクトファイル:%1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory includeディレクトリを選択 Select a directory to check チェックするディレクトリを選択してください Clang-tidy (not found) Import Project Select directory to ignore 除外するディレクトリを選択してください Add Suppression 抑制する指摘を追加 Select error id suppress: 抑制するエラーID(error id)を選択してください 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 (Not found) QPlatformTheme OK OK Cancel キャンセル Close 閉じる Save 保存する ResultsTree File ファイル Severity 警告種別 Line Summary 内容 Undefined file 未定義ファイル [Inconclusive] [結論の出ない] debug デバッグ note Recheck Copy filename ファイル名をコピー Copy full path フルパスをコピー Copy message メッセージをコピー Copy message id Hide 非表示 Hide all with id 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. メニューの「編集」→「設定」からテキストファイルを表示するアプリケーションを設定してください。 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. Summary 内容 Message メッセージ First included by Id ID Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad 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) ... 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> 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 Select clang path Select include directory include ディレクトリを選択 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: 情報メッセージ History File: Copy to Clipboard クリップボードにコピー Pdf Export 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 Project Settings プロジェクトの設定 Paths パス Include paths Defines 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 結論の出ない cppcheck-1.82/gui/cppcheck_ko.ts000066400000000000000000002001401322667425100166360ustar00rootroot00000000000000 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-2018 Cppcheck team. 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 Can not open file %1. Can not save file %1. Save the library as Failed to load %1. %2. 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 이 존재하지 않습니다! 최근 프로젝트 목록에서 파일을 제거하시겠습니까? Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 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 C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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) 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 Suppression list: Add Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Defines must be separated by a semicolon ';' ... Include Paths: Paths and Defines Import Project (Visual studio / compile database) <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 Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy ProjectFileDialog Project file: %1 프로젝트 파일: %1 Select include directory Include 디렉토리 선택 Select a directory to check 검사할 디렉토리 선택 Select directory to ignore 무시할 디렉토리 선택 Add Suppression Select error id suppress: Select Cppcheck build dir Import Project Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Clang-tidy (not found) 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) 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 Copy message id Hide all with id Open containing folder Inconclusive Recheck note Suppress selected id(s) Tag No tag Since date 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 검사 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> SettingsDialog N/A N/A Add a new application 새 응용 프로그램 추가 Modify an application 응용 프로그램 편집 [Default] [기본] Select include directory Include 디렉토리 선택 [Default] Select python binary Select clang path 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 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 불확실 cppcheck-1.82/gui/cppcheck_nl.ts000066400000000000000000002663341322667425100166570ustar00rootroot00000000000000 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-2018 Cppcheck team. Copyright © 2007-2018 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 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 Can not open file %1. Failed to load %1. %2. 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 &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: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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! 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? 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 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) 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. ... <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: Paden: Add... Toevoegen... Edit Bewerk Remove Verwijder Include Paths: Includes Inclusief Include directories: Include mappen: Up Omhoog Down Omlaag Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Exclusief Suppressions Suppression list: Add Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: Omschrijft: ProjectFileDialog Project file: %1 Project Bestand %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Selecteer include map Select a directory to check Selecteer een map om te controleren Clang-tidy (not found) Import Project Select directory to ignore Selecteer een map om te negeren Add Suppression Select error id suppress: 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) QPlatformTheme OK Cancel Annuleer Close Sluit Save Opslaan ResultsTree File Bestand Severity Ernst Line Regel Summary Overzicht Undefined file Niet gedefinieerd bestand [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 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) ... 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> 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 clang path Select include directory Selecteer include map StatsDialog Statistics Statistieken Project Project Project: Project: Paths: Paden: Include paths: Bevat paden: Defines: Omschrijft: 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 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 cppcheck-1.82/gui/cppcheck_ru.ts000066400000000000000000003112271322667425100166640ustar00rootroot00000000000000 About About Cppcheck О Cppcheck Version %1 Версия %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - программа для статического анализа кода на языках С/С++. Copyright © 2007-2018 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 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 Can not open file %1. Невозможно открыть файл %1. Failed to load %1. %2. 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 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 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++ E&nforce C C++14 C++14 &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: Быстрый фильтр: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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. 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! 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? 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 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 - Command line parameters Cppcheck GUI - параметры Командной строки 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) 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. Note: Положите свои .cfg-файлы в одну директорию с файлом проекта. Вы увидите их сверху. ... <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: Пути: Add... Добавить... Edit Изменить Remove Удалить Include Paths: Includes Пути для заголовочных файлов Include directories: Пути для поиска заголовочных файлов: Up Вверх Down Вниз Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Исключенные пути Suppressions Suppression list: Add Добавить Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: Макросы: ProjectFileDialog Project file: %1 Файл проекта: %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Выберите директорию для поиска заголовочных файлов Select a directory to check Выберите директорию для проверки Clang-tidy (not found) Import Project Select directory to ignore Выберите директорию, которую надо проигнорировать Add Suppression Select error id suppress: 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) QPlatformTheme OK OK Cancel Отмена Close Закрыть Save Сохранить ResultsTree File Файл Severity Важность Line Строка Summary Кратко Undefined file Неопределенный файл [Inconclusive] [Неубедительный] debug отлаживать note Recheck Проверить заново Copy filename Скопировать имя файла Copy full path Скопировать полный путь Copy message Скопировать сообщение Copy message id Скопировать номер сообщения Hide Скрыть Hide all with id Скрыть все с 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 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. 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 Блокнот 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) ... 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> 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 Select clang path Select include directory Выберите директорию StatsDialog Statistics Статистика Project Проект Project: Проект: Paths: Пути: Include paths: Включенные пути: Defines: Макросы: 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 день %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 Project Settings Настройки проекта Paths Пути Include paths Включенные пути Defines Макросы 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 незначительная cppcheck-1.82/gui/cppcheck_sr.ts000066400000000000000000002545551322667425100166740ustar00rootroot00000000000000 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-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 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 Can not open file %1. Failed to load %1. %2. 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 &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: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) 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. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 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! 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? 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 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 Project Paths and Defines Import Project (Visual studio / compile database) 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. ... <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: Add... Edit Remove Include Paths: Up Down Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Suppressions Suppression list: Add Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: ProjectFileDialog Project file: %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Select a directory to check Clang-tidy (not found) Import Project Select directory to ignore Add Suppression Select error id suppress: 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) QPlatformTheme OK Cancel Close Save ResultsTree File File Severity Severity Line Line Summary Undefined file Undefined file [Inconclusive] debug note Recheck Copy filename Copy filename Copy full path Copy full path Copy message Copy message 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. 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. Could not find file: %1 Please select the directory where file is located. 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. Summary Message 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 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) ... 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> 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 clang path StatsDialog Statistics Project Project: Paths: Include paths: Defines: 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 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 cppcheck-1.82/gui/cppcheck_sv.ts000066400000000000000000003034421322667425100166660ustar00rootroot00000000000000 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-2018 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 Hemsida: %1 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 Can not open file %1. Kunde ej öppna filen %1. Failed to load %1. %2. 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 &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! 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? 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 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) Importera Projekt (Visual Studio / compile database) 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> Analyze all Visual Studio configurations Analysera alla Visual Studio konfigurationer Paths: Sökvägar: Add... Lägg till... Edit Redigera Remove Ta bort Include Paths: Include sökvägar: Includes Include Include directories: Include sökvägar Up Upp Down Ned Root path: Bas sökväg: Warning tags (separated by semicolon) Varnings taggar (separerade med semikolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck build dir (whole program analys, incremental analys, statistik, etc) Libraries Libraries Exclude Exkludera Suppressions Suppressions Suppression list: Suppression-list: Add Lägg till 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 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 a directory to check Välj mapp att analysera 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) QPlatformTheme OK OK Cancel Avbryt Close Stäng Save Spara ResultsTree File Fil Severity Typ Line Rad Summary Sammanfattning Undefined file Odefinierad fil [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 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) ... ... 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> 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 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: 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 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 cppcheck-1.82/gui/cppcheck_zh_CN.ts000066400000000000000000002652611322667425100172450ustar00rootroot00000000000000 About About Cppcheck 关于 Cppcheck Version %1 版本 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - C/C++ 静态代码分析工具。 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 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 Can not open file %1. Failed to load %1. %2. 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 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) 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! 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? 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 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) 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. ... <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: 路径: Add... 添加... Edit 编辑 Remove 移除 Include Paths: Includes 包含 Include directories: Include 目录: Up 向上 Down 向下 Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude 排除 Suppressions Suppression list: Add Addons Y2038 Thread safety Coding standards Cert Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Defines: 定义: ProjectFileDialog Project file: %1 项目文件: %1 Select Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory 选择 Include 目录 Select a directory to check 选择一个检查目录 Clang-tidy (not found) Import Project Select directory to ignore 选择忽略的目录 Add Suppression Select error id suppress: 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) QPlatformTheme OK Cancel Close 关闭 Save ResultsTree File 文件 Severity 严重性 Line Summary 概要 Undefined file 未定义文件 [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 便条 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) ... 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> 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 clang path Select include directory 选择包含目录 StatsDialog Statistics 统计 Project 项目 Project: 项目: Paths: 路径: Include paths: 包含路径: Defines: 定义: 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 定义 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 不确定的 cppcheck-1.82/gui/cppchecklibrarydata.cpp000066400000000000000000000506251322667425100205330ustar00rootroot00000000000000/* * 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 "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 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 == "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 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.82/gui/cppchecklibrarydata.h000066400000000000000000000114741322667425100201770ustar00rootroot00000000000000/* * 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 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(); functions.clear(); memoryresource.clear(); podtypes.clear(); } void swap(CppcheckLibraryData &other) { containers.swap(other.containers); defines.swap(other.defines); 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; }; #endif // LIBRARYDATA_H cppcheck-1.82/gui/csvreport.cpp000066400000000000000000000032541322667425100165570ustar00rootroot00000000000000/* * 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.82/gui/csvreport.h000066400000000000000000000034371322667425100162270ustar00rootroot00000000000000/* * 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 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(); /** * @brief Write report header. */ virtual void writeHeader(); /** * @brief Write report footer. */ virtual void writeFooter(); /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error); private: /** * @brief Text stream writer for writing the report in text format. */ QTextStream mTxtWriter; }; /// @} #endif // CSV_REPORT_H cppcheck-1.82/gui/erroritem.cpp000066400000000000000000000047501322667425100165420ustar00rootroot00000000000000/* * 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 "erroritem.h" #include "common.h" QErrorPathItem::QErrorPathItem(const ErrorLogger::ErrorMessage::FileLocation &loc) : file(QString::fromStdString(loc.getfile(false))) , line(loc.line) , col(loc.col) , info(QString::fromStdString(loc.getinfo())) { } bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2) { return i1.file == i2.file && i1.col == i2.col && 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) { 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; } cppcheck-1.82/gui/erroritem.h000066400000000000000000000057411322667425100162100ustar00rootroot00000000000000/* * 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 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), col(-1) {} explicit QErrorPathItem(const ErrorLogger::ErrorMessage::FileLocation &loc); QString file; unsigned int line; int col; 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; // Special GUI properties QString sinceDate; QString tags; }; 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.82/gui/file.ui000066400000000000000000000030171322667425100152770ustar00rootroot00000000000000 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.82/gui/filelist.cpp000066400000000000000000000073531322667425100163470ustar00rootroot00000000000000/* * 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 "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.canonicalFilePath()); 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.82/gui/filelist.h000066400000000000000000000061211322667425100160040ustar00rootroot00000000000000/* * 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.82/gui/fileviewdialog.cpp000066400000000000000000000042131322667425100175160ustar00rootroot00000000000000/* * 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.82/gui/fileviewdialog.h000066400000000000000000000030201322667425100171560ustar00rootroot00000000000000/* * 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 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 = 0); 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.82/gui/gui.cppcheck000066400000000000000000000004441322667425100163100ustar00rootroot00000000000000 cppcheck-1.82/gui/gui.pro000066400000000000000000000102571322667425100153330ustar00rootroot00000000000000lessThan(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 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 \ 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 SOURCES += aboutdialog.cpp \ application.cpp \ applicationdialog.cpp \ applicationlist.cpp \ checkstatistics.cpp \ checkthread.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 win32 { RC_FILE = cppcheck-gui.rc HEADERS += ../lib/version.h contains(LINKCORE, [yY][eE][sS]) { } else { LIBS += -lshlwapi } } contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++0x -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare } 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.82/gui/gui.qrc000066400000000000000000000025031322667425100153130ustar00rootroot00000000000000 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.82/gui/help/000077500000000000000000000000001322667425100147505ustar00rootroot00000000000000cppcheck-1.82/gui/help/buildhelp.bat000066400000000000000000000002741322667425100174130ustar00rootroot00000000000000@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.82/gui/help/manual.html000066400000000000000000000410031322667425100171110ustar00rootroot00000000000000 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.82/gui/help/online-help.qhcp000066400000000000000000000006241322667425100200410ustar00rootroot00000000000000 online-help.qhp online-help.qch online-help.qch cppcheck-1.82/gui/help/online-help.qhp000066400000000000000000000006231322667425100176750ustar00rootroot00000000000000 cppcheck.sourceforge.net doc
manual.html cppcheck-1.82/gui/images/000077500000000000000000000000001322667425100152655ustar00rootroot00000000000000cppcheck-1.82/gui/images/applications-development.png000066400000000000000000000022771322667425100230110ustar00rootroot00000000000000PNG  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.82/gui/images/dialog-error.png000066400000000000000000000014511322667425100203620ustar00rootroot00000000000000PNG  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.82/gui/images/dialog-information.png000066400000000000000000000017561322667425100215660ustar00rootroot00000000000000PNG  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.82/gui/images/dialog-warning.png000066400000000000000000000014451322667425100207010ustar00rootroot00000000000000PNG  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.82/gui/images/edit-clear.png000066400000000000000000000021531322667425100200050ustar00rootroot00000000000000PNG  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.82/gui/images/go-down.png000066400000000000000000000013561322667425100173520ustar00rootroot00000000000000PNG  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.82/gui/images/go-previous.png000066400000000000000000000015211322667425100202510ustar00rootroot00000000000000PNG  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.82/gui/images/llvm-dragon.png000066400000000000000000000224121322667425100202160ustar00rootroot00000000000000PNG  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.82/gui/images/llvm-dragon.svg000066400000000000000000003476761322667425100202570ustar00rootroot00000000000000Dragoncppcheck-1.82/gui/images/media-floppy.png000066400000000000000000000012371322667425100203640ustar00rootroot00000000000000PNG  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.82/gui/images/openproject.png000066400000000000000000000017751322667425100203350ustar00rootroot00000000000000PNG  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.82/gui/images/scratchpad.png000066400000000000000000000004111322667425100201030ustar00rootroot00000000000000PNG  IHDR Tg-PLTEIIIOO>qtM̔ɰ|tRNS@fIDATx!@EgvѠ"<l- $+TT5{9:^~k}`Ӳf6u0 C 0 C=p`o0$'eWlIENDB`cppcheck-1.82/gui/images/showerrors.png000066400000000000000000000014511322667425100202110ustar00rootroot00000000000000PNG  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.82/gui/images/showperformance.png000066400000000000000000000015171322667425100212010ustar00rootroot00000000000000PNG  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.82/gui/images/showstylewarnings.png000066400000000000000000000017561322667425100216160ustar00rootroot00000000000000PNG  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.82/gui/images/showwarnings.png000066400000000000000000000014451322667425100205300ustar00rootroot00000000000000PNG  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.82/gui/images/text-x-generic.png000066400000000000000000000006171322667425100206420ustar00rootroot00000000000000PNG  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.82/gui/images/view-recheck.png000066400000000000000000000023451322667425100203530ustar00rootroot00000000000000PNG  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.82/gui/images/view-refresh.png000066400000000000000000000011421322667425100203770ustar00rootroot00000000000000PNG  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.82/gui/libraryaddfunctiondialog.cpp000066400000000000000000000012571322667425100215740ustar00rootroot00000000000000#include "libraryaddfunctiondialog.h" #include "ui_libraryaddfunctiondialog.h" #include #include LibraryAddFunctionDialog::LibraryAddFunctionDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LibraryAddFunctionDialog) { ui->setupUi(this); QRegExp rx(NAMES); QValidator *validator = new QRegExpValidator(rx, this); ui->functionName->setValidator(validator); } LibraryAddFunctionDialog::~LibraryAddFunctionDialog() { delete ui; } QString LibraryAddFunctionDialog::functionName() const { return ui->functionName->text(); } int LibraryAddFunctionDialog::numberOfArguments() const { return ui->numberOfArguments->value(); } cppcheck-1.82/gui/libraryaddfunctiondialog.h000066400000000000000000000013321322667425100212330ustar00rootroot00000000000000#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 = 0); ~LibraryAddFunctionDialog(); QString functionName() const; int numberOfArguments() const; private: Ui::LibraryAddFunctionDialog *ui; }; #endif // LIBRARYADDFUNCTIONDIALOG_H cppcheck-1.82/gui/libraryaddfunctiondialog.ui000066400000000000000000000056531322667425100214330ustar00rootroot00000000000000 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.82/gui/librarydialog.cpp000066400000000000000000000254741322667425100173640ustar00rootroot00000000000000/* * 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 "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), ui(new Ui::LibraryDialog), ignoreChanges(false) { ui->setupUi(this); ui->buttonSave->setEnabled(false); ui->buttonSaveAs->setEnabled(false); ui->sortFunctions->setEnabled(false); ui->filter->setEnabled(false); ui->addFunction->setEnabled(false); //As no function selected, this disables function editing widgets selectFunction(); } LibraryDialog::~LibraryDialog() { delete ui; } CppcheckLibraryData::Function *LibraryDialog::currentFunction() { QList selitems = ui->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("Can not 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; } ignoreChanges = true; data.swap(tempdata); mFileName = selectedFile; ui->buttonSave->setEnabled(false); ui->buttonSaveAs->setEnabled(true); ui->filter->clear(); ui->functions->clear(); for (CppcheckLibraryData::Function &function : data.functions) { ui->functions->addItem(new FunctionListItem(ui->functions, &function, false)); } ui->sortFunctions->setEnabled(!data.functions.empty()); ui->filter->setEnabled(!data.functions.empty()); ui->addFunction->setEnabled(true); ignoreChanges = false; } void LibraryDialog::saveCfg() { if (mFileName.isNull()) return; QFile file(mFileName); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream ts(&file); ts << data.toString() << '\n'; ui->buttonSave->setEnabled(false); } else { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Can not 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); } data.functions.append(f); ui->functions->addItem(new FunctionListItem(ui->functions, &data.functions.back(), false)); ui->buttonSave->setEnabled(true); ui->sortFunctions->setEnabled(!data.functions.empty()); ui->filter->setEnabled(!data.functions.empty()); } delete d; } void LibraryDialog::editFunctionName(QListWidgetItem* item) { if (ignoreChanges) 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; ui->buttonSave->setEnabled(true); } else { ignoreChanges = true; item->setText(function->name); ignoreChanges = false; } } } void LibraryDialog::selectFunction() { const CppcheckLibraryData::Function * const function = currentFunction(); if (function == nullptr) { ui->comments->clear(); ui->comments->setEnabled(false); ui->noreturn->setCurrentIndex(0); ui->noreturn->setEnabled(false); ui->useretval->setChecked(false); ui->useretval->setEnabled(false); ui->leakignore->setChecked(false); ui->leakignore->setEnabled(false); ui->arguments->clear(); ui->arguments->setEnabled(false); ui->editArgButton->setEnabled(false); return; } ignoreChanges = true; ui->comments->setPlainText(function->comments); ui->comments->setEnabled(true); ui->noreturn->setCurrentIndex(function->noreturn); ui->noreturn->setEnabled(true); ui->useretval->setChecked(function->useretval); ui->useretval->setEnabled(true); ui->leakignore->setChecked(function->leakignore); ui->leakignore->setEnabled(true); updateArguments(*function); ui->arguments->setEnabled(true); ui->editArgButton->setEnabled(true); ignoreChanges = false; } void LibraryDialog::sortFunctions(bool sort) { if (sort) { ui->functions->sortItems(); } else { ignoreChanges = true; CppcheckLibraryData::Function *selfunction = currentFunction(); ui->functions->clear(); for (CppcheckLibraryData::Function &function : data.functions) { ui->functions->addItem(new FunctionListItem(ui->functions, &function, selfunction == &function)); } if (!ui->filter->text().isEmpty()) filterFunctions(ui->filter->text()); ignoreChanges = false; } } void LibraryDialog::filterFunctions(QString filter) { QList allItems = ui->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 (ignoreChanges) return; CppcheckLibraryData::Function *function = currentFunction(); if (!function) return; function->comments = ui->comments->toPlainText(); function->noreturn = (CppcheckLibraryData::Function::TrueFalseUnknown)ui->noreturn->currentIndex(); function->useretval = ui->useretval->isChecked(); function->leakignore = ui->leakignore->isChecked(); ui->buttonSave->setEnabled(true); } void LibraryDialog::editArg() { CppcheckLibraryData::Function *function = currentFunction(); if (!function) return; if (ui->arguments->selectedItems().count() != 1) return; CppcheckLibraryData::Function::Arg &arg = function->args[ui->arguments->row(ui->arguments->selectedItems().first())]; LibraryEditArgDialog *d = new LibraryEditArgDialog(0, arg); if (d->exec() == QDialog::Accepted) { unsigned number = arg.nr; arg = d->getArg(); arg.nr = number; ui->arguments->selectedItems().first()->setText(getArgText(arg)); } delete d; ui->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) { ui->arguments->clear(); foreach (const CppcheckLibraryData::Function::Arg &arg, function.args) { ui->arguments->addItem(getArgText(arg)); } } cppcheck-1.82/gui/librarydialog.h000066400000000000000000000032521322667425100170170ustar00rootroot00000000000000/* * 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 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 = 0); ~LibraryDialog(); 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 *ui; CppcheckLibraryData data; QString mFileName; bool ignoreChanges; static QString getArgText(const CppcheckLibraryData::Function::Arg &arg); CppcheckLibraryData::Function *currentFunction(); void updateArguments(const CppcheckLibraryData::Function &function); }; #endif // LIBRARYDIALOG_H cppcheck-1.82/gui/librarydialog.ui000066400000000000000000000323361322667425100172120ustar00rootroot00000000000000 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.82/gui/libraryeditargdialog.cpp000066400000000000000000000074101322667425100207120ustar00rootroot00000000000000#include "libraryeditargdialog.h" #include "ui_libraryeditargdialog.h" LibraryEditArgDialog::LibraryEditArgDialog(QWidget *parent, const CppcheckLibraryData::Function::Arg &arg) : QDialog(parent), ui(new Ui::LibraryEditArgDialog), minsizes(arg.minsizes) { ui->setupUi(this); ui->notbool->setChecked(arg.notbool); ui->notnull->setChecked(arg.notnull); ui->notuninit->setChecked(arg.notuninit); ui->strz->setChecked(arg.strz); ui->formatstr->setChecked(arg.formatstr); ui->valid->setText(arg.valid); ui->minsize1type->setEnabled(true); ui->minsize1arg->setEnabled(arg.minsizes.count() >= 1); ui->minsize1arg2->setEnabled(arg.minsizes.count() >= 1 && arg.minsizes[0].type == "mul"); ui->minsize2type->setEnabled(arg.minsizes.count() >= 1); ui->minsize2arg->setEnabled(arg.minsizes.count() >= 2); ui->minsize2arg2->setEnabled(arg.minsizes.count() >= 2 && arg.minsizes[1].type == "mul"); QStringList items; items << "None" << "argvalue" << "mul" << "sizeof" << "strlen"; ui->minsize1type->clear(); ui->minsize1type->addItems(items); if (arg.minsizes.count() >= 1) { ui->minsize1type->setCurrentIndex(items.indexOf(minsizes[0].type)); ui->minsize1arg->setValue(minsizes[0].arg.toInt()); if (arg.minsizes[0].type == "mul") ui->minsize1arg2->setValue(minsizes[0].arg2.toInt()); else ui->minsize1arg2->setValue(0); } else { ui->minsize1type->setCurrentIndex(0); ui->minsize1arg->setValue(0); ui->minsize1arg2->setValue(0); } ui->minsize2type->clear(); ui->minsize2type->addItems(items); if (arg.minsizes.count() >= 2) { ui->minsize2type->setCurrentIndex(items.indexOf(minsizes[1].type)); ui->minsize2arg->setValue(minsizes[1].arg.toInt()); if (arg.minsizes[1].type == "mul") ui->minsize2arg2->setValue(minsizes[1].arg2.toInt()); else ui->minsize2arg2->setValue(0); } else { ui->minsize2type->setCurrentIndex(0); ui->minsize2arg->setValue(0); ui->minsize2arg2->setValue(0); } } LibraryEditArgDialog::~LibraryEditArgDialog() { delete ui; } CppcheckLibraryData::Function::Arg LibraryEditArgDialog::getArg() const { CppcheckLibraryData::Function::Arg ret; ret.notbool = ui->notbool->isChecked(); ret.notnull = ui->notnull->isChecked(); ret.notuninit = ui->notuninit->isChecked(); ret.strz = ui->strz->isChecked(); ret.formatstr = ui->formatstr->isChecked(); if (ui->minsize1type->currentIndex() != 0) { CppcheckLibraryData::Function::Arg::MinSize minsize1; minsize1.type = ui->minsize1type->currentText(); minsize1.arg = QString::number(ui->minsize1arg->value()); if (minsize1.type == "mul") minsize1.arg2 = QString::number(ui->minsize1arg2->value()); ret.minsizes.append(minsize1); if (ui->minsize2type->currentIndex() != 0) { CppcheckLibraryData::Function::Arg::MinSize minsize2; minsize2.type = ui->minsize2type->currentText(); minsize2.arg = QString::number(ui->minsize2arg->value()); if (minsize2.type == "mul") minsize2.arg2 = QString::number(ui->minsize2arg2->value()); ret.minsizes.append(minsize2); } } ret.valid = ui->valid->text(); return ret; } void LibraryEditArgDialog::minsizeChanged(int) { ui->minsize1arg->setEnabled(ui->minsize1type->currentIndex() != 0); ui->minsize1arg2->setEnabled(ui->minsize1type->currentText() == "mul"); ui->minsize2type->setEnabled(ui->minsize1type->currentIndex() != 0); ui->minsize2arg->setEnabled(ui->minsize2type->currentIndex() != 0); ui->minsize2arg2->setEnabled(ui->minsize2type->currentText() == "mul"); } cppcheck-1.82/gui/libraryeditargdialog.h000066400000000000000000000011321322667425100203520ustar00rootroot00000000000000#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(); CppcheckLibraryData::Function::Arg getArg() const; private slots: void minsizeChanged(int); private: Ui::LibraryEditArgDialog *ui; QList minsizes; }; #endif // LIBRARYEDITARGDIALOG_H cppcheck-1.82/gui/libraryeditargdialog.ui000066400000000000000000000247571322667425100205620ustar00rootroot00000000000000 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.82/gui/main.cpp000066400000000000000000000115571322667425100154610ustar00rootroot00000000000000/* * 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 #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); #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.82/gui/mainwindow.cpp000066400000000000000000001725561322667425100167200ustar00rootroot00000000000000/* * 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 #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:")); 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.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); mUI.mActionPrint->setEnabled(false); mUI.mActionPrintPreview->setEnabled(false); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); 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 plat = mPlatforms.mPlatforms[i]; QAction *act = new QAction(this); plat.mActMainWindow = act; mPlatforms.mPlatforms[i] = plat; act->setText(plat.mTitle); act->setData(plat.mType); act->setCheckable(true); act->setActionGroup(mPlatformActions); mUI.mMenuAnalyze->insertAction(mUI.mActionPlatforms, act); connect(act, 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.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 defaultPlat = Settings::Win32W; #else const Settings::PlatformType defaultPlat = Settings::Unspecified; #endif Platform &plat = mPlatforms.get((Settings::PlatformType)mSettings->value(SETTINGS_CHECKED_PLATFORM, defaultPlat).toInt()); plat.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); const bool stdCpp03 = mSettings->value(SETTINGS_STD_CPP03, false).toBool(); mUI.mActionCpp03->setChecked(stdCpp03); const bool stdCpp11 = mSettings->value(SETTINGS_STD_CPP11, true).toBool(); mUI.mActionCpp11->setChecked(stdCpp11 && !stdCpp03); const bool stdCpp14 = mSettings->value(SETTINGS_STD_CPP14, true).toBool(); mUI.mActionCpp14->setChecked(stdCpp14 && !stdCpp03 && !stdCpp11); const bool stdC89 = mSettings->value(SETTINGS_STD_C89, false).toBool(); mUI.mActionC89->setChecked(stdC89); const bool stdC11 = mSettings->value(SETTINGS_STD_C11, false).toBool(); mUI.mActionC11->setChecked(stdC11); const bool stdC99 = mSettings->value(SETTINGS_STD_C99, true).toBool(); mUI.mActionC99->setChecked(stdC99 || (!stdC89 && !stdC11)); const bool stdPosix = mSettings->value(SETTINGS_STD_POSIX, false).toBool(); mUI.mActionPosix->setChecked(stdPosix); // 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(); } } } 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()); mSettings->setValue(SETTINGS_STD_CPP03, mUI.mActionCpp03->isChecked()); mSettings->setValue(SETTINGS_STD_CPP11, mUI.mActionCpp11->isChecked()); mSettings->setValue(SETTINGS_STD_CPP14, mUI.mActionCpp14->isChecked()); mSettings->setValue(SETTINGS_STD_C89, mUI.mActionC89->isChecked()); mSettings->setValue(SETTINGS_STD_C99, mUI.mActionC99->isChecked()); mSettings->setValue(SETTINGS_STD_C11, mUI.mActionC11->isChecked()); mSettings->setValue(SETTINGS_STD_POSIX, mUI.mActionPosix->isChecked()); // 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) { 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()) { std::set filenames; Settings::PlatformType platform = (Settings::PlatformType) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt(); for (std::list::iterator it = p.fileSettings.begin(); it != p.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 = p.fileSettings.erase(it); } else { filenames.insert(fs.filename); ++it; } } } } 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; 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()); 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) { 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); 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(); 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 &)), this, SLOT(log(const QString &))); connect(&result, SIGNAL(debugError(const ErrorItem &)), this, 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(); } 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) { selected = QFileDialog::getOpenFileNames(this, tr("Select files to analyze"), getPath(SETTINGS_LAST_CHECK_PATH), tr("C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj)") .arg(FileList::getDefaultFilters().join(" ")) .arg(compile_commands_json)); 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)) { 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, 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 CFGDIR // Try to load the library from CFGDIR.. const QString cfgdir = CFGDIR; if (!cfgdir.isEmpty()) { ret = library->load(nullptr, (cfgdir+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(nullptr, (cfgdir+"/cfg/"+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; // 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 libraries = mProjectFile->getLibraries(); foreach (QString library, libraries) { const QString filename = library + ".cfg"; tryLoadLibrary(&result.library, filename); } const QStringList suppressions = mProjectFile->getSuppressions(); foreach (QString suppression, suppressions) { result.nomsg.addSuppressionLine(suppression.toStdString()); } // Only check the given -D configuration if (!defines.isEmpty()) result.maxConfigs = 1; 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(); } } } // 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.debug = false; 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(); result.platformType = (Settings::PlatformType) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt(); if (mSettings->value(SETTINGS_STD_CPP03, false).toBool()) result.standards.cpp = Standards::CPP03; else if (mSettings->value(SETTINGS_STD_CPP11, false).toBool()) result.standards.cpp = Standards::CPP11; else if (mSettings->value(SETTINGS_STD_CPP14, true).toBool()) result.standards.cpp = Standards::CPP14; result.standards.c = mSettings->value(SETTINGS_STD_C99, true).toBool() ? Standards::C99 : (mSettings->value(SETTINGS_STD_C11, false).toBool() ? Standards::C11 : Standards::C89); result.standards.posix = mSettings->value(SETTINGS_STD_POSIX, false).toBool(); result.enforcedLang = (Settings::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); const bool std = tryLoadLibrary(&result.library, "std.cfg"); bool posix = true; if (result.standards.posix) posix = tryLoadLibrary(&result.library, "posix.cfg"); bool windows = true; if (result.platformType == Settings::Win32A || result.platformType == Settings::Win32W || result.platformType == Settings::Win64) 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 (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); 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); } } if (mUI.mResults->hasResults()) { mUI.mActionClearResults->setEnabled(true); mUI.mActionSave->setEnabled(true); mUI.mActionPrint->setEnabled(true); mUI.mActionPrintPreview->setEnabled(true); } 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(); mUI.mResults->updateSettings(dialog.showFullPath(), dialog.saveFullPath(), dialog.saveAllErrors(), dialog.showNoErrorsMessage(), dialog.showErrorId(), dialog.showInconclusive()); 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::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); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); mUI.mActionPrint->setEnabled(false); mUI.mActionPrintPreview->setEnabled(false); } 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." "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()) { 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); } } 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::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(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 lastPath = mSettings->value(SETTINGS_LAST_PROJECT_PATH, QString()).toString(); 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()); return true; } void MainWindow::analyzeProject(const ProjectFile *projectFile) { Settings::terminate(false); QFileInfo inf(projectFile->getFilename()); const QString rootpath = projectFile->getRootPath(); mThread->setAddonsAndTools(projectFile->getAddonsAndTools()); 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 = mCurrentDirectory + '/' + 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 = 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); 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; } // Convert relative paths to absolute paths for (int i = 0; i < paths.size(); i++) { if (!QDir::isAbsolutePath(paths[i])) { QString path = mCurrentDirectory + "/"; path += paths[i]; paths[i] = QDir::cleanPath(path); } } doAnalyzeFiles(paths); } 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); } void MainWindow::enableProjectOpenActions(bool enable) { mUI.mActionNewProjectFile->setEnabled(enable); mUI.mActionOpenProjectFile->setEnabled(enable); } void MainWindow::openRecentProject() { QAction *action = qobject_cast(sender()); if (action) { const QString project = action->data().toString(); QFileInfo inf(project); if (inf.exists()) { loadProjectFile(project); } 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 empty or space only items int removed = projects.removeDuplicates(); for (int i = projects.size() - 1; i >= 0; i--) { QString text = projects[i].trimmed(); if (text.isEmpty()) { 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) { QStringList suppressions = mProjectFile->getSuppressions(); foreach (QString s, ids) { if (!suppressions.contains(s)) suppressions << s; } mProjectFile->setSuppressions(suppressions); mProjectFile->write(); } } cppcheck-1.82/gui/mainwindow.h000066400000000000000000000304121322667425100163450ustar00rootroot00000000000000/* * 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 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); virtual ~MainWindow(); /** * 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 selected files * @param selectedFilesList list of selected files */ void performSelectedFilesCheck(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. */ void analyzeProject(const ProjectFile *projectFile); /** * @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 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 */ void doAnalyzeProject(ImportProject p); /** * @brief Analyze all files specified in parameter files * * @param files List of files and/or directories to analyze */ void doAnalyzeFiles(const QStringList &files); /** * @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, 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.82/gui/mainwindow.ui000066400000000000000000000543561322667425100165500ustar00rootroot00000000000000 MainWindow 0 0 640 480 0 0 640 480 Cppcheck 22 22 0 0 0 0 16777215 16777215 0 0 640 25 &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... :/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 true C++14 ResultsView QWidget
resultsview.h
1
cppcheck-1.82/gui/platforms.cpp000066400000000000000000000032471322667425100165410ustar00rootroot00000000000000/* * 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.82/gui/platforms.h000066400000000000000000000030211322667425100161740ustar00rootroot00000000000000/* * 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 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 = NULL); 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.82/gui/printablereport.cpp000066400000000000000000000030531322667425100177410ustar00rootroot00000000000000/* * 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.82/gui/printablereport.h000066400000000000000000000033461322667425100174130ustar00rootroot00000000000000/* * 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 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(); /** * @brief Write report header. */ virtual void writeHeader(); /** * @brief Write report footer. */ virtual void writeFooter(); /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error); /** * @brief Returns the formatted report. */ QString getFormattedReportText() const; private: /** * @brief Stores the formatted report contents. */ QString mFormattedReport; }; /// @} #endif // PRINTABLE_REPORT_H cppcheck-1.82/gui/projectfile.cpp000066400000000000000000000516011322667425100170350ustar00rootroot00000000000000/* * 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 #include "projectfile.h" #include "common.h" static const char ProjectElementName[] = "project"; static const char ProjectVersionAttrib[] = "version"; static const char ProjectFileVersion[] = "1"; static const char BuildDirElementName[] = "builddir"; static const char ImportProjectElementName[] = "importproject"; static const char AnalyzeAllVsConfigsElementName[] = "analyze-all-vs-configs"; static const char IncludeDirElementName[] = "includedir"; static const char DirElementName[] = "dir"; static const char DirNameAttrib[] = "name"; static const char DefinesElementName[] = "defines"; static const char DefineName[] = "define"; static const char DefineNameAttrib[] = "name"; static const char PathsElementName[] = "paths"; static const char PathName[] = "dir"; static const char PathNameAttrib[] = "name"; static const char RootPathName[] = "root"; static const char RootPathNameAttrib[] = "name"; static const char IgnoreElementName[] = "ignore"; static const char IgnorePathName[] = "path"; static const char IgnorePathNameAttrib[] = "name"; static const char ExcludeElementName[] = "exclude"; static const char ExcludePathName[] = "path"; static const char ExcludePathNameAttrib[] = "name"; static const char LibrariesElementName[] = "libraries"; static const char LibraryElementName[] = "library"; static const char SuppressionsElementName[] = "suppressions"; static const char SuppressionElementName[] = "suppression"; static const char AddonElementName[] = "addon"; static const char AddonsElementName[] = "addons"; static const char ToolElementName[] = "tool"; static const char ToolsElementName[] = "tools"; static const char TagsElementName[] = "tags"; static const char TagElementName[] = "tag"; 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(); mPaths.clear(); mExcludedPaths.clear(); mLibraries.clear(); mSuppressions.clear(); mAddons.clear(); mClangAnalyzer = mClangTidy = false; } 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() == ProjectElementName) { insideProject = true; projectTagFound = true; } // Read root path from inside project element if (insideProject && xmlReader.name() == RootPathName) readRootPath(xmlReader); // Read root path from inside project element if (insideProject && xmlReader.name() == BuildDirElementName) readBuildDir(xmlReader); // Find paths to check from inside project element if (insideProject && xmlReader.name() == PathsElementName) readCheckPaths(xmlReader); if (insideProject && xmlReader.name() == ImportProjectElementName) readImportProject(xmlReader); if (insideProject && xmlReader.name() == AnalyzeAllVsConfigsElementName) readAnalyzeAllVsConfigs(xmlReader); // Find include directory from inside project element if (insideProject && xmlReader.name() == IncludeDirElementName) readIncludeDirs(xmlReader); // Find preprocessor define from inside project element if (insideProject && xmlReader.name() == DefinesElementName) readDefines(xmlReader); // Find exclude list from inside project element if (insideProject && xmlReader.name() == ExcludeElementName) readExcludes(xmlReader); // Find ignore list from inside project element // These are read for compatibility if (insideProject && xmlReader.name() == IgnoreElementName) readExcludes(xmlReader); // Find libraries list from inside project element if (insideProject && xmlReader.name() == LibrariesElementName) readStringList(mLibraries, xmlReader,LibraryElementName); // Find suppressions list from inside project element if (insideProject && xmlReader.name() == SuppressionsElementName) readStringList(mSuppressions, xmlReader,SuppressionElementName); // Addons if (insideProject && xmlReader.name() == AddonsElementName) readStringList(mAddons, xmlReader, AddonElementName); // Tools if (insideProject && xmlReader.name() == ToolsElementName) { QStringList tools; readStringList(tools, xmlReader, ToolElementName); mClangAnalyzer = tools.contains(CLANG_ANALYZER); mClangTidy = tools.contains(CLANG_TIDY); } if (insideProject && xmlReader.name() == TagsElementName) readStringList(mTags, xmlReader, TagElementName); break; case QXmlStreamReader::EndElement: if (xmlReader.name() == 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(), 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); } void ProjectFile::readAnalyzeAllVsConfigs(QXmlStreamReader &reader) { do { const QXmlStreamReader::TokenType type = reader.readNext(); switch (type) { case QXmlStreamReader::Characters: mAnalyzeAllVsConfigs = (reader.text().toString() == "true"); 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::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() == DirElementName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), DirNameAttrib).toString(); if (!name.isEmpty()) mIncludeDirs << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == 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() == DefineName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), DefineNameAttrib).toString(); if (!name.isEmpty()) mDefines << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == 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() == PathName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), PathNameAttrib).toString(); if (!name.isEmpty()) mPaths << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == 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() == ExcludePathName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), ExcludePathNameAttrib).toString(); if (!name.isEmpty()) mExcludedPaths << name; } // Read ignore-elements - deprecated but support reading them else if (reader.name().toString() == IgnorePathName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), IgnorePathNameAttrib).toString(); if (!name.isEmpty()) mExcludedPaths << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == IgnoreElementName) allRead = true; if (reader.name().toString() == 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::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::setCheckPaths(const QStringList &paths) { mPaths = paths; } void ProjectFile::setExcludedPaths(const QStringList &paths) { mExcludedPaths = paths; } void ProjectFile::setLibraries(const QStringList &libraries) { mLibraries = libraries; } void ProjectFile::setSuppressions(const QStringList &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(ProjectElementName); xmlWriter.writeAttribute(ProjectVersionAttrib, ProjectFileVersion); if (!mRootPath.isEmpty()) { xmlWriter.writeStartElement(RootPathName); xmlWriter.writeAttribute(RootPathNameAttrib, mRootPath); xmlWriter.writeEndElement(); } if (!mBuildDir.isEmpty()) { xmlWriter.writeStartElement(BuildDirElementName); xmlWriter.writeCharacters(mBuildDir); xmlWriter.writeEndElement(); } if (!mImportProject.isEmpty()) { xmlWriter.writeStartElement(ImportProjectElementName); xmlWriter.writeCharacters(mImportProject); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement(AnalyzeAllVsConfigsElementName); xmlWriter.writeCharacters(mAnalyzeAllVsConfigs ? "true" : "false"); xmlWriter.writeEndElement(); if (!mIncludeDirs.isEmpty()) { xmlWriter.writeStartElement(IncludeDirElementName); foreach (QString incdir, mIncludeDirs) { xmlWriter.writeStartElement(DirElementName); xmlWriter.writeAttribute(DirNameAttrib, incdir); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mDefines.isEmpty()) { xmlWriter.writeStartElement(DefinesElementName); foreach (QString define, mDefines) { xmlWriter.writeStartElement(DefineName); xmlWriter.writeAttribute(DefineNameAttrib, define); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mPaths.isEmpty()) { xmlWriter.writeStartElement(PathsElementName); foreach (QString path, mPaths) { xmlWriter.writeStartElement(PathName); xmlWriter.writeAttribute(PathNameAttrib, path); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mExcludedPaths.isEmpty()) { xmlWriter.writeStartElement(ExcludeElementName); foreach (QString path, mExcludedPaths) { xmlWriter.writeStartElement(ExcludePathName); xmlWriter.writeAttribute(ExcludePathNameAttrib, path); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } writeStringList(xmlWriter, mLibraries, LibrariesElementName, LibraryElementName); writeStringList(xmlWriter, mSuppressions, SuppressionsElementName, SuppressionElementName); writeStringList(xmlWriter, mAddons, AddonsElementName, AddonElementName); QStringList tools; if (mClangAnalyzer) tools << CLANG_ANALYZER; if (mClangTidy) tools << CLANG_TIDY; writeStringList(xmlWriter, tools, ToolsElementName, ToolElementName); writeStringList(xmlWriter, mTags, TagsElementName, 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; } cppcheck-1.82/gui/projectfile.h000066400000000000000000000216351322667425100165060ustar00rootroot00000000000000/* * 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 PROJECT_FILE_H #define PROJECT_FILE_H #include #include #include #include /// @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 = 0); ProjectFile(const QString &filename, QObject *parent = 0); /** * @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; } /** * @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 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 list suppressions. * @return list of suppressions. */ QStringList 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; } /** * @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 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 list of suppressions. * @param suppressions List of suppressions. */ void setSuppressions(const QStringList &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; } 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); void readAnalyzeAllVsConfigs(QXmlStreamReader &reader); /** * @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 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; /** * @brief List of include directories used to search include files. */ QStringList mIncludeDirs; /** * @brief List of defines. */ QStringList mDefines; /** * @brief List of paths to check. */ QStringList mPaths; /** * @brief Paths excluded from the check. */ QStringList mExcludedPaths; /** * @brief List of libraries. */ QStringList mLibraries; /** * @brief List of suppressions. */ QStringList mSuppressions; /** * @brief List of addons. */ QStringList mAddons; /** @brief Execute clang analyzer? */ bool mClangAnalyzer; /** @brief Execute clang-tidy? */ bool mClangTidy; /** * @brief Warning tags */ QStringList mTags; }; /// @} #endif // PROJECT_FILE_H cppcheck-1.82/gui/projectfile.txt000066400000000000000000000035211322667425100170700ustar00rootroot00000000000000Project 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.82/gui/projectfiledialog.cpp000066400000000000000000000457641322667425100202320ustar00rootroot00000000000000/* * 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 #include #include #include #include #include "common.h" #include "projectfiledialog.h" #include "checkthread.h" #include "projectfile.h" #include "library.h" #include "cppcheck.h" #include "errorlogger.h" 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; #ifdef CFGDIR const QString cfgdir = CFGDIR; #endif const QString datadir = settings.value("DATADIR",QString()).toString(); QStringList searchPaths; searchPaths << appPath << appPath + "/cfg" << inf.canonicalPath(); #ifdef CFGDIR if (!cfgdir.isEmpty()) searchPaths << cfgdir << cfgdir + "/cfg"; #endif if (!datadir.isEmpty()) searchPaths << datadir << datadir + "/cfg"; QStringList libs; 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; const Library::Error 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; } } qSort(libs); foreach (const QString library, libs) { QCheckBox *checkbox = new QCheckBox(this); checkbox->setText(library); mUI.mLayoutLibraries->addWidget(checkbox); mLibraryCheckboxes << checkbox; } mUI.mEditTags->setValidator(new QRegExpValidator(QRegExp("[a-zA-Z0-9 ;]*"),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); 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) { 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()); setCheckPaths(projectFile->getCheckPaths()); setImportProject(projectFile->getImportProject()); mUI.mChkAllVsConfigs->setChecked(projectFile->getAnalyzeAllVsConfigs()); setExcludedPaths(projectFile->getExcludedPaths()); setLibraries(projectFile->getLibraries()); setSuppressions(projectFile->getSuppressions()); 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"); mUI.mAddonY2038->setChecked(projectFile->getAddons().contains("y2038")); mUI.mAddonCert->setChecked(projectFile->getAddons().contains("cert")); 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); } QString tags; foreach (const QString tag, projectFile->getTags()) { if (tags.isEmpty()) tags = tag; else tags += ';' + tag; } mUI.mEditTags->setText(tags); updatePathsAndDefines(); } void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const { projectFile->setRootPath(getRootPath()); projectFile->setBuildDir(getBuildDir()); projectFile->setImportProject(getImportProject()); projectFile->setAnalyzeAllVsConfigs(mUI.mChkAllVsConfigs->isChecked()); projectFile->setIncludes(getIncludePaths()); projectFile->setDefines(getDefines()); projectFile->setCheckPaths(getCheckPaths()); projectFile->setExcludedPaths(getExcludedPaths()); projectFile->setLibraries(getLibraries()); projectFile->setSuppressions(getSuppressions()); QStringList list; if (mUI.mAddonThreadSafety->isChecked()) list << "threadsafety"; if (mUI.mAddonY2038->isChecked()) list << "y2038"; if (mUI.mAddonCert->isChecked()) list << "cert"; projectFile->setAddons(list); projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked()); projectFile->setClangTidy(mUI.mToolClangTidy->isChecked()); QStringList tags(mUI.mEditTags->text().split(";")); tags.removeAll(QString()); projectFile->setTags(tags); } 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.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(); QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"), dir.canonicalPath(), tr("Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json)")); 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 { const int count = mUI.mListIncludeDirs->count(); QStringList includePaths; for (int i = 0; i < count; i++) { QListWidgetItem *item = mUI.mListIncludeDirs->item(i); includePaths << QDir::fromNativeSeparators(item->text()); } return includePaths; } QStringList ProjectFileDialog::getDefines() const { QString define = mUI.mEditDefines->text(); QStringList defines; if (!define.isEmpty()) { define = define.trimmed(); if (define.indexOf(';') != -1) defines = define.split(";"); else defines.append(define); } return defines; } QStringList ProjectFileDialog::getCheckPaths() const { const int count = mUI.mListCheckPaths->count(); QStringList paths; for (int i = 0; i < count; i++) { QListWidgetItem *item = mUI.mListCheckPaths->item(i); paths << QDir::fromNativeSeparators(item->text()); } return paths; } QStringList ProjectFileDialog::getExcludedPaths() const { const int count = mUI.mListExcludedPaths->count(); QStringList paths; for (int i = 0; i < count; i++) { QListWidgetItem *item = mUI.mListExcludedPaths->item(i); paths << QDir::fromNativeSeparators(item->text()); } return paths; } QStringList ProjectFileDialog::getLibraries() const { QStringList libraries; foreach (const QCheckBox *checkbox, mLibraryCheckboxes) { if (checkbox->isChecked()) libraries << checkbox->text(); } return libraries; } QStringList ProjectFileDialog::getSuppressions() const { QStringList suppressions; const int count = mUI.mListSuppressions->count(); for (int i = 0; i < count; i++) { QListWidgetItem *item = mUI.mListSuppressions->item(i); suppressions << item->text(); } return suppressions; } 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) { QString definestr; QString define; foreach (define, defines) { definestr += define; definestr += ";"; } // Remove ; from the end of the string if (definestr.endsWith(';')) definestr = definestr.left(definestr.length() - 1); mUI.mEditDefines->setText(definestr); } 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 i = 0; i < mLibraryCheckboxes.size(); i++) { QCheckBox *checkbox = mLibraryCheckboxes[i]; checkbox->setChecked(libraries.contains(checkbox->text())); } } void ProjectFileDialog::setSuppressions(const QStringList &suppressions) { mUI.mListSuppressions->clear(); mUI.mListSuppressions->addItems(suppressions); 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() { 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(); bool ok; QString item = QInputDialog::getItem(this, tr("Add Suppression"), tr("Select error id suppress:"), errorLogger.errorIds, 0, false, &ok); if (ok && !item.isEmpty()) { mUI.mListSuppressions->addItem(item); mUI.mListSuppressions->sortItems(); } } void ProjectFileDialog::removeSuppression() { const int row = mUI.mListSuppressions->currentRow(); QListWidgetItem *item = mUI.mListSuppressions->takeItem(row); delete item; } cppcheck-1.82/gui/projectfiledialog.h000066400000000000000000000145421322667425100176650ustar00rootroot00000000000000/* * 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 PROJECTFILE_DIALOG_H #define PROJECTFILE_DIALOG_H #include #include #include #include #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: ProjectFileDialog(ProjectFile *projectFile, QWidget *parent = 0); 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 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. */ QStringList getSuppressions() const; /** * @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 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 QStringList &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(); 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); private: Ui::ProjectFile mUI; /** * @brief Projectfile path. */ ProjectFile *mProjectFile; /** @brief Library checkboxes */ QList mLibraryCheckboxes; QString getExistingDirectory(const QString &caption, bool trailingSlash); }; /// @} #endif // PROJECTFILE_DIALOG_H cppcheck-1.82/gui/projectfiledialog.ui000066400000000000000000000440111322667425100200450ustar00rootroot00000000000000 ProjectFile 0 0 642 507 Project File 0 Paths and Defines Import Project (Visual studio / compile database) true false :/images/edit-clear.png ... <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 ';' Include Paths: Qt::Vertical 20 40 QAbstractItemView::SelectRows Add... Edit Remove Qt::Vertical 20 40 Up Down Qt::Vertical 20 0 Project Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) ... 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 Exclude Paths: Add... Edit Remove Qt::Vertical 20 40 Suppressions Suppression list: Qt::Horizontal 40 20 Add Remove Addons Y2038 Thread safety Coding standards Cert Qt::Vertical 20 368 Extra Tools It is common best practice to use several tools. Clang analyzer Clang-tidy Qt::Vertical 20 310 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok mButtons mListExcludedPaths mBtnAddIgnorePath mBtnEditIgnorePath mBtnRemoveIgnorePath mButtons accepted() ProjectFile accept() 270 352 157 158 mButtons rejected() ProjectFile reject() 338 352 286 158 cppcheck-1.82/gui/readme.txt000066400000000000000000000055241322667425100160240ustar00rootroot00000000000000Cppcheck 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.82/gui/report.cpp000066400000000000000000000027041322667425100160420ustar00rootroot00000000000000/* * 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.82/gui/report.h000066400000000000000000000041051322667425100155040ustar00rootroot00000000000000/* * 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 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). */ virtual 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.82/gui/resultstree.cpp000066400000000000000000001260261322667425100171140ustar00rootroot00000000000000/* * 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 #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(0), mShowFullPath(false), mSaveFullPath(false), mSaveAllErrors(true), mShowErrorId(false), mVisibleErrors(false), mSelectionModel(0), 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 = !mShowSeverities.isShown(item.severity); //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["col"] = loc.col; data["id"] = item.errorId; data["inconclusive"] = item.inconclusive; data["file0"] = stripPath(item.file0, true); data["sinceDate"] = item.sinceDate; data["tags"] = item.tags; 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["col"] = e.col; 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(), false); } 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 0; } 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 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 menuitems to copy full path/filename to clipboard if (mContextItem) { if (mApplications->getApplicationCount() > 0) { menu.addSeparator(); } //Create an action for the application QAction *recheckSelectedFiles = new QAction(tr("Recheck"), &menu); QAction *copyfilename = new QAction(tr("Copy filename"), &menu); QAction *copypath = new QAction(tr("Copy full path"), &menu); QAction *copymessage = new QAction(tr("Copy message"), &menu); QAction *copymessageid = new QAction(tr("Copy message id"), &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) { copyfilename->setDisabled(true); copypath->setDisabled(true); copymessage->setDisabled(true); copymessageid->setDisabled(true); hideallid->setDisabled(true); opencontainingfolder->setDisabled(true); } if (mThread->isChecking()) recheckSelectedFiles->setDisabled(true); else recheckSelectedFiles->setDisabled(false); menu.addAction(recheckSelectedFiles); menu.addSeparator(); menu.addAction(copyfilename); menu.addAction(copypath); menu.addAction(copymessage); menu.addAction(copymessageid); menu.addSeparator(); menu.addAction(hide); menu.addAction(hideallid); menu.addAction(suppress); menu.addSeparator(); menu.addAction(opencontainingfolder); connect(recheckSelectedFiles, SIGNAL(triggered()), this, SLOT(recheckSelectedFiles())); connect(copyfilename, SIGNAL(triggered()), this, SLOT(copyFilename())); connect(copypath, SIGNAL(triggered()), this, SLOT(copyFullPath())); connect(copymessage, SIGNAL(triggered()), this, SLOT(copyMessage())); connect(copymessageid, SIGNAL(triggered()), this, SLOT(copyMessageId())); 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%1\nPlease select the directory where file is located.").arg(file); QMessageBox msgbox(this); msgbox.setWindowTitle("Cppcheck"); msgbox.setText(text); msgbox.setIcon(QMessageBox::Warning); msgbox.exec(); QString dir = QFileDialog::getExistingDirectory(this, tr("Select Directory"), getPath(SETTINGS_LAST_SOURCE_PATH), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); mCheckPath = dir; setPath(SETTINGS_LAST_SOURCE_PATH, dir); return dir; } void ResultsTree::copyFilename() { copyPathToClipboard(mContextItem, false); } void ResultsTree::copyFullPath() { copyPathToClipboard(mContextItem, true); } void ResultsTree::copyMessage() { if (mContextItem) { // 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 message; if (data["inconclusive"].toBool()) { message = tr("[Inconclusive]"); message += " "; } message += data["message"].toString(); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(message); } } void ResultsTree::copyMessageId() { if (mContextItem) { // 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(); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(messageId); } } 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()) { // 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(); // 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)); } void ResultsTree::copyPathToClipboard(QStandardItem *target, bool fullPath) { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(getFilePath(target, fullPath)); } 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 (list[i].errorId == item.errorId && list[i].errorPath == item.errorPath && list[i].file0 == item.file0 && list[i].message == item.message && list[i].inconclusive == item.inconclusive && list[i].severity == item.severity) { 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 selectionChanged(current); } cppcheck-1.82/gui/resultstree.h000066400000000000000000000337701322667425100165640ustar00rootroot00000000000000/* * 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 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 = 0); 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 selectionChanged(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 filename to clipboard * */ void copyFilename(); /** * @brief Slot for context menu item to copy full path to clipboard * */ void copyFullPath(); /** * @brief Slot for context menu item to the current error message to clipboard * */ void copyMessage(); /** * @brief Slot for context menu item to the current error message Id to clipboard * */ void copyMessageId(); /** * @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; QItemSelectionModel *mSelectionModel; ThreadHandler *mThread; bool mShowCppcheck; bool mShowClang; }; /// @} #endif // RESULTSTREE_H cppcheck-1.82/gui/resultsview.cpp000066400000000000000000000323231322667425100171230ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "common.h" #include "erroritem.h" #include "resultsview.h" #include "report.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" 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::selectionChanged, 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); 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 = NULL; 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 = NULL; } 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::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; } XmlReport *report = new XmlReportV2(filename); QList errors; if (report) { if (report->open()) errors = report->read(); else { QMessageBox msgBox; msgBox.setText(tr("Failed to read the report.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); } delete report; report = NULL; } 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); } mUI.mTree->setCheckDirectory(QString()); } void ResultsView::updateDetails(const QModelIndex &index) { QStandardItemModel *model = qobject_cast(mUI.mTree->model()); QStandardItem *item = model->itemFromIndex(index); 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 summary = data["summary"].toString(); const QString message = data["message"].toString(); QString formattedMsg = QString("%1: %2\n%3: %4") .arg(tr("Summary")).arg(summary) .arg(tr("Message")).arg(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); } 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) { 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.82/gui/resultsview.h000066400000000000000000000201261322667425100165660ustar00rootroot00000000000000/* * 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 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 = 0); void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler); virtual ~ResultsView(); 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 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.82/gui/resultsview.ui000066400000000000000000000077541322667425100167700ustar00rootroot00000000000000 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 false true ResultsTree QTreeView
resultstree.h
mTree mDetails
cppcheck-1.82/gui/scratchpad.cpp000066400000000000000000000023601322667425100166410ustar00rootroot00000000000000/* * 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.82/gui/scratchpad.h000066400000000000000000000023461322667425100163120ustar00rootroot00000000000000/* * 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.82/gui/scratchpad.ui000066400000000000000000000046541322667425100165040ustar00rootroot00000000000000 ScratchPad 0 0 500 600 Scratchpad Courier New 10 0 Qt::Horizontal 40 20 filename Check 0 0 Qt::Horizontal QDialogButtonBox::Close mButtonBox rejected() ScratchPad reject() 20 20 20 20 cppcheck-1.82/gui/settings.ui000066400000000000000000000327361322667425100162320ustar00rootroot00000000000000 Settings 0 0 589 319 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) ... 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 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() 248 254 157 274 mButtons rejected() Settings reject() 316 260 286 274 cppcheck-1.82/gui/settingsdialog.cpp000066400000000000000000000263071322667425100175540ustar00rootroot00000000000000/* * 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 #include #include "settingsdialog.h" #include "applicationdialog.h" #include "applicationlist.h" #include "translationhandler.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()); #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 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); 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(LangCodeRole, 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()); #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(LangCodeRole).toString(); settings.setValue(SETTINGS_LANGUAGE, langcode); } } 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 = 0; 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::browseClangPath() { QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select clang path"), QDir::rootPath()); if (!selectedDir.isEmpty()) { mUI.mEditClangPath->setText(selectedDir); } } cppcheck-1.82/gui/settingsdialog.h000066400000000000000000000112701322667425100172120ustar00rootroot00000000000000/* * 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 SETTINGSDIALOG_H #define SETTINGSDIALOG_H #include #include "ui_settings.h" class QSettings; class QWidget; class ApplicationList; class TranslationHandler; /// @addtogroup GUI /// @{ /** * @brief Settings dialog * */ class SettingsDialog : public QDialog { Q_OBJECT public: SettingsDialog(ApplicationList *list, TranslationHandler *translator, QWidget *parent = 0); virtual ~SettingsDialog(); /** * @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(); 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 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: static const int LangCodeRole = Qt::UserRole; }; /// @} #endif // SETTINGSDIALOG_H cppcheck-1.82/gui/showtypes.cpp000066400000000000000000000073661322667425100166050ustar00rootroot00000000000000/* * 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.82/gui/showtypes.h000066400000000000000000000066471322667425100162530ustar00rootroot00000000000000/* * 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.82/gui/stats.ui000066400000000000000000000320231322667425100155150ustar00rootroot00000000000000 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 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.82/gui/statsdialog.cpp000077500000000000000000000412111322667425100170440ustar00rootroot00000000000000/* * 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 #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(";")); #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()); } } 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*)0, 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 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" ) .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()); 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" "
%2:%3
%4:%5
%6:%7
%8:%9
\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()); 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.82/gui/statsdialog.h000066400000000000000000000041461322667425100165140ustar00rootroot00000000000000/* * 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 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 = 0); /** * @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.82/gui/test/000077500000000000000000000000001322667425100147775ustar00rootroot00000000000000cppcheck-1.82/gui/test/benchmark/000077500000000000000000000000001322667425100167315ustar00rootroot00000000000000cppcheck-1.82/gui/test/benchmark/benchmark.pro000066400000000000000000000000701322667425100214020ustar00rootroot00000000000000CONFIG += ordered TEMPLATE = subdirs SUBDIRS = simple cppcheck-1.82/gui/test/benchmark/simple/000077500000000000000000000000001322667425100202225ustar00rootroot00000000000000cppcheck-1.82/gui/test/benchmark/simple/benchmarksimple.cpp000066400000000000000000000043041322667425100240730ustar00rootroot00000000000000/* * 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 #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.82/gui/test/benchmark/simple/benchmarksimple.h000066400000000000000000000023111322667425100235340ustar00rootroot00000000000000/* * 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 #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) { } void reportErr(const ErrorLogger::ErrorMessage &msg) { } }; cppcheck-1.82/gui/test/benchmark/simple/simple.pro000066400000000000000000000003741322667425100222410ustar00rootroot00000000000000TEMPLATE = 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.82/gui/test/common.pri000066400000000000000000000013771322667425100170130ustar00rootroot00000000000000Qt += testlib QT += widgets QT += printsupport INCLUDEPATH += $${PWD}/.. LIBS += -L$$PWD/../../externals INCLUDEPATH += $${PWD}/../../externals include($${PWD}/../../lib/lib.pri) # GUI SOURCES += $${PWD}/../erroritem.cpp \ $${PWD}/../filelist.cpp \ $${PWD}/../projectfile.cpp \ $${PWD}/../report.cpp \ $${PWD}/../translationhandler.cpp \ $${PWD}/../xmlreport.cpp \ $${PWD}/../xmlreportv2.cpp HEADERS += $${PWD}/../erroritem.h \ $${PWD}/../filelist.h \ $${PWD}/../projectfile.h \ $${PWD}/../report.h \ $${PWD}/../translationhandler.h \ $${PWD}/../xmlreport.h \ $${PWD}/../xmlreportv2.h contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++11 } contains(QMAKE_CXX, clang++) { QMAKE_CXXFLAGS += -std=c++11 } cppcheck-1.82/gui/test/data/000077500000000000000000000000001322667425100157105ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/benchmark/000077500000000000000000000000001322667425100176425ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/benchmark/simple.cpp000066400000000000000000004105201322667425100216410ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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 insert 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.82/gui/test/data/files/000077500000000000000000000000001322667425100170125ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/files/bar1000066400000000000000000000000211322667425100175530ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/bar1.foo000066400000000000000000000000211322667425100203350ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/dir1/000077500000000000000000000000001322667425100176515ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/files/dir1/dir11/000077500000000000000000000000001322667425100205715ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/files/dir1/dir11/foo11.cpp000066400000000000000000000000211322667425100222130ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/dir1/foo1.cpp000066400000000000000000000000211322667425100212120ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/dir2/000077500000000000000000000000001322667425100176525ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/files/dir2/foo1.cpp000066400000000000000000000000211322667425100212130ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo1.cpp000066400000000000000000000000211322667425100203530ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo2.cxx000066400000000000000000000000211322667425100203740ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo3.cc000066400000000000000000000000211322667425100201600ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo4.c000066400000000000000000000000211322667425100200160ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo5.c++000066400000000000000000000000211322667425100201450ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo6.txx000066400000000000000000000000211322667425100204210ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/files/foo7.tpp000066400000000000000000000000211322667425100204020ustar00rootroot00000000000000Dummy test file. cppcheck-1.82/gui/test/data/projectfiles/000077500000000000000000000000001322667425100204015ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/projectfiles/simple.cppcheck000066400000000000000000000006031322667425100233730ustar00rootroot00000000000000 cppcheck-1.82/gui/test/data/projectfiles/simple_ignore.cppcheck000066400000000000000000000006011322667425100247340ustar00rootroot00000000000000 cppcheck-1.82/gui/test/data/projectfiles/simple_noroot.cppcheck000066400000000000000000000005501322667425100247740ustar00rootroot00000000000000 cppcheck-1.82/gui/test/data/xmlfiles/000077500000000000000000000000001322667425100175335ustar00rootroot00000000000000cppcheck-1.82/gui/test/data/xmlfiles/xmlreport_v2.xml000066400000000000000000000040141322667425100227170ustar00rootroot00000000000000 cppcheck-1.82/gui/test/filelist/000077500000000000000000000000001322667425100166125ustar00rootroot00000000000000cppcheck-1.82/gui/test/filelist/filelist.pro000066400000000000000000000003521322667425100211470ustar00rootroot00000000000000TEMPLATE = app TARGET = test-filelist DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testfilelist.cpp HEADERS += testfilelist.h cppcheck-1.82/gui/test/filelist/testfilelist.cpp000066400000000000000000000142411322667425100220330ustar00rootroot00000000000000/* * 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 "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.82/gui/test/filelist/testfilelist.h000066400000000000000000000021361322667425100215000ustar00rootroot00000000000000/* * 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 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.82/gui/test/projectfile/000077500000000000000000000000001322667425100173055ustar00rootroot00000000000000cppcheck-1.82/gui/test/projectfile/projectfile.pro000066400000000000000000000003631322667425100223370ustar00rootroot00000000000000TEMPLATE = app TARGET = test-projectfile DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testprojectfile.cpp HEADERS += testprojectfile.h cppcheck-1.82/gui/test/projectfile/testprojectfile.cpp000066400000000000000000000070711322667425100232240ustar00rootroot00000000000000/* * 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 "testprojectfile.h" #include "projectfile.h" 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.82/gui/test/projectfile/testprojectfile.h000066400000000000000000000017151322667425100226700ustar00rootroot00000000000000/* * 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 class TestProjectFile: public QObject { Q_OBJECT private slots: void loadInexisting(); void loadSimple(); void loadSimpleWithIgnore(); void loadSimpleNoroot(); }; cppcheck-1.82/gui/test/readme.txt000066400000000000000000000013071322667425100167760ustar00rootroot00000000000000GUI 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.82/gui/test/test.pro000066400000000000000000000003511322667425100164770ustar00rootroot00000000000000#lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) CONFIG += ordered TEMPLATE = subdirs SUBDIRS = benchmark \ filelist \ projectfile \ translationhandler \ xmlreport \ xmlreportv2 cppcheck-1.82/gui/test/translationhandler/000077500000000000000000000000001322667425100206735ustar00rootroot00000000000000cppcheck-1.82/gui/test/translationhandler/testtranslationhandler.cpp000066400000000000000000000021201322667425100261660ustar00rootroot00000000000000/* * 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 "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.82/gui/test/translationhandler/testtranslationhandler.h000066400000000000000000000015721322667425100256450ustar00rootroot00000000000000/* * 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 class TestTranslationHandler: public QObject { Q_OBJECT private slots: void construct(); }; cppcheck-1.82/gui/test/translationhandler/translationhandler.pro000066400000000000000000000003471322667425100253150ustar00rootroot00000000000000TEMPLATE = app TARGET = test-translationhandler DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) # tests SOURCES += testtranslationhandler.cpp HEADERS += testtranslationhandler.h cppcheck-1.82/gui/test/xmlreport/000077500000000000000000000000001322667425100170335ustar00rootroot00000000000000cppcheck-1.82/gui/test/xmlreport/testxmlreport.cpp000066400000000000000000000032071322667425100224750ustar00rootroot00000000000000/* * 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 #include "testxmlreport.h" #include "xmlreport.h" void TestXmlReport::testQuoteMessage() { const QString toQuote("abcdefgh&\"'<>12345"); const QString quoted("abcdefgh&"'<>12345"); QCOMPARE(XmlReport::quoteMessage(toQuote), quoted); } void TestXmlReport::testUnquoteMessage() { const QString toQuote("abcdefgh&\"'<>12345"); const QString quoted("abcdefgh&"'<>12345"); QCOMPARE(XmlReport::unquoteMessage(quoted), toQuote); } void TestXmlReport::testGetVersion1() { const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v1.xml"); QCOMPARE(XmlReport::determineVersion(filepath), 1); } void TestXmlReport::testGetVersion2() { const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v2.xml"); QCOMPARE(XmlReport::determineVersion(filepath), 2); } QTEST_MAIN(TestXmlReport) cppcheck-1.82/gui/test/xmlreport/testxmlreport.h000066400000000000000000000017171322667425100221460ustar00rootroot00000000000000/* * 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 class TestXmlReport: public QObject { Q_OBJECT private slots: void testQuoteMessage(); void testUnquoteMessage(); void testGetVersion1(); void testGetVersion2(); }; cppcheck-1.82/gui/test/xmlreport/xmlreport.pro000066400000000000000000000003551322667425100216140ustar00rootroot00000000000000TEMPLATE = app TARGET = test-xmlreport DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testxmlreport.cpp HEADERS += testxmlreport.h cppcheck-1.82/gui/test/xmlreportv2/000077500000000000000000000000001322667425100173035ustar00rootroot00000000000000cppcheck-1.82/gui/test/xmlreportv2/testxmlreportv2.cpp000066400000000000000000000043741322667425100232230ustar00rootroot00000000000000/* * 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 "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.82/gui/test/xmlreportv2/testxmlreportv2.h000066400000000000000000000015611322667425100226630ustar00rootroot00000000000000/* * 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 class TestXmlReportV2: public QObject { Q_OBJECT private slots: void readXml(); }; cppcheck-1.82/gui/test/xmlreportv2/xmlreportv2.pro000066400000000000000000000003631322667425100223330ustar00rootroot00000000000000TEMPLATE = app TARGET = test-xmlreportv2 DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testxmlreportv2.cpp HEADERS += testxmlreportv2.h cppcheck-1.82/gui/threadhandler.cpp000066400000000000000000000161611322667425100173360ustar00rootroot00000000000000/* * 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 "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]->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.82/gui/threadhandler.h000066400000000000000000000135271322667425100170060ustar00rootroot00000000000000/* * 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 THREADHANDLER_H #define THREADHANDLER_H #include #include #include #include #include "threadresult.h" #include "importproject.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 = 0); 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) { mAddonsAndTools = addonsAndTools; } void setSuppressions(const QStringList &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; QStringList mSuppressions; QStringList mClangIncludePaths; QString mDataDir; private: /** * @brief Check if a file needs to be rechecked. Recursively checks * included headers. Used by GetReCheckFiles() */ bool needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const; }; /// @} #endif // THREADHANDLER_H cppcheck-1.82/gui/threadresult.cpp000066400000000000000000000071241322667425100172360ustar00rootroot00000000000000/* * 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 #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.82/gui/threadresult.h000066400000000000000000000062471322667425100167100ustar00rootroot00000000000000/* * 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 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); void reportErr(const ErrorLogger::ErrorMessage &msg); 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.82/gui/translationhandler.cpp000066400000000000000000000151671322667425100204320ustar00rootroot00000000000000/* * 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 #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() { #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(NULL) { // 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 = NULL; } 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; } // 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 = 0); 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.82/gui/txtreport.cpp000066400000000000000000000040451322667425100166020ustar00rootroot00000000000000/* * 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 "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 && !error.errorPath.isEmpty()) { 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.82/gui/txtreport.h000066400000000000000000000032721322667425100162500ustar00rootroot00000000000000/* * 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 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(); /** * @brief Write report header. */ virtual void writeHeader(); /** * @brief Write report footer. */ virtual void writeFooter(); /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error); private: /** * @brief Text stream writer for writing the report in text format. */ QTextStream mTxtWriter; }; /// @} #endif // TXT_REPORT_H cppcheck-1.82/gui/xmlreport.cpp000066400000000000000000000056541322667425100165720ustar00rootroot00000000000000/* * 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.82/gui/xmlreport.h000066400000000000000000000034301322667425100162250ustar00rootroot00000000000000/* * 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.82/gui/xmlreportv2.cpp000066400000000000000000000237611322667425100170410ustar00rootroot00000000000000/* * 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 #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 ColAttribute = "col"; 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 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(NULL), mXmlWriter(NULL) { } 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].col > 0) mXmlWriter->writeAttribute(ColAttribute, QString::number(error.errorPath[i].col)); 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(), ColAttribute)) loc.col = attribs.value(QString(), ColAttribute).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.82/gui/xmlreportv2.h000066400000000000000000000044031322667425100164760ustar00rootroot00000000000000/* * 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_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(); /** * @brief Open existing report file. */ bool open(); /** * @brief Write report header. */ virtual void writeHeader(); /** * @brief Write report footer. */ virtual void writeFooter(); /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error); /** * @brief Read contents of the report file. */ virtual QList read(); 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.82/htmlreport/000077500000000000000000000000001322667425100154345ustar00rootroot00000000000000cppcheck-1.82/htmlreport/README.txt000066400000000000000000000006611322667425100171350ustar00rootroot00000000000000cppcheck-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.82/htmlreport/check.sh000077500000000000000000000030151322667425100170470ustar00rootroot00000000000000#!/bin/bash -ex ./cppcheck-htmlreport --file ../gui/test/data/xmlfiles/xmlreport_v1.xml --title "xml1 test" --report-dir . --source-dir ../test/ ./cppcheck-htmlreport --file ../gui/test/data/xmlfiles/xmlreport_v2.xml --title "xml2 test" --report-dir . --source-dir ../test/ echo -e "\n" ../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 "" ../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" ../cppcheck --errorlist --inconclusive --xml-version=2 > errorlist.xml xmllint --noout errorlist.xml ./cppcheck-htmlreport --file ./errorlist.xml --title "errorlist" --report-dir . ../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 cppcheck-1.82/htmlreport/cppcheck-htmlreport000077500000000000000000000611041322667425100213420ustar00rootroot00000000000000#!/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_for_filename from pygments.formatters import HtmlFormatter 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: 200px; 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: 200px; } .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)), 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'] }) def handleVersion2(self, name, attributes): if name == 'cppcheck': self.versionCppcheck = attributes['version'] if name == 'error': # is there a better solution than this? if ('inconclusive' in attributes and 'cwe' in attributes): self.errors.append({ 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'verbose': attributes.get('verbose'), 'inconclusive': attributes['inconclusive'], 'cwe': attributes['cwe'] }) elif 'inconclusive' in attributes: self.errors.append({ 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'verbose': attributes.get('verbose'), 'inconclusive': attributes['inconclusive'] }) elif 'cwe' in attributes: self.errors.append({ 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'verbose': attributes.get('verbose'), 'cwe': attributes['cwe'] }) else: self.errors.append({ 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'verbose': attributes.get('verbose') }) elif name == 'location': assert self.errors self.errors[-1]['file'] = attributes['file'] self.errors[-1]['line'] = int(attributes['line']) 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.mkdir(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 = data['errors'] 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: 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 = " {}{}" 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(' ') output_file.write(' ') output_file.write(''.join(stat_html)) output_file.write(' ') output_file.write('
Show#Defect ID
' + str(stats_count) + 'total
') output_file.write(' Statistics

') output_file.write(HTML_HEAD_END.replace("content", "content_index", 1)) output_file.write(' \n') output_file.write( ' ') 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'], error['msg'])) elif (error['id'] == 'unmatchedSuppression') and filename.endswith('*'): output_file.write( '\n ' % (error['id'], error['id'], error['severity'], error_class, error['msg'])) else: output_file.write( '\n ' % (error['id'], data['htmlfile'], error['line'], error['line'], error['id'], cwe_url, error['severity'], error_class, 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): break 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") print("\nOpen '" + options.report_dir + "/index.html' to see the results.") cppcheck-1.82/htmlreport/example.cc000066400000000000000000000000711322667425100173740ustar00rootroot00000000000000#include "missing.h" int main() { int x; x++; } cppcheck-1.82/htmlreport/example.xml000066400000000000000000000007531322667425100176160ustar00rootroot00000000000000 Checking example.cc... Checking usage of global functions.. cppcheck-1.82/htmlreport/requirements.txt000066400000000000000000000000111322667425100207100ustar00rootroot00000000000000Pygments cppcheck-1.82/htmlreport/setup.py000077500000000000000000000010301322667425100171430ustar00rootroot00000000000000#!/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.82/htmlreport/test_htmlreport.py000077500000000000000000000070071322667425100212540ustar00rootroot00000000000000#!/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 ['1', '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.c_str()); for (std::list::const_iterator f = sourcefiles.begin(); f != sourcefiles.end(); ++f) { const std::string afile = getFilename(*f); if (fileCount.find(afile) == fileCount.end()) fileCount[afile] = 0; fout << afile << ".a" << (++fileCount[afile]) << "::" << Path::fromNativeSeparators(*f) << '\n'; } for (std::list::const_iterator fs = fileSettings.begin(); fs != fileSettings.end(); ++fs) { const std::string afile = getFilename(fs->filename); if (fileCount.find(afile) == fileCount.end()) fileCount[afile] = 0; fout << afile << ".a" << (++fileCount[afile]) << ":" << fs->cfg << ":" << Path::fromNativeSeparators(fs->filename) << std::endl; } } void AnalyzerInformation::close() { analyzerInfoFile.clear(); if (fout.is_open()) { fout << "\n"; fout.close(); } } static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long checksum, std::list *errors) { tinyxml2::XMLDocument doc; 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->push_back(ErrorLogger::ErrorMessage(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.c_str()); if (fin.is_open()) { std::string line; const std::string endsWith(':' + cfg + ':' + sourcefile); while (std::getline(fin,line)) { if (line.size() <= endsWith.size() + 2U) continue; if (line.compare(line.size()-endsWith.size(), endsWith.size(), endsWith) != 0) 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(); analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg); if (skipAnalysis(analyzerInfoFile, checksum, errors)) return false; fout.open(analyzerInfoFile); if (fout.is_open()) { fout << "\n"; fout << "\n"; } else { analyzerInfoFile.clear(); } return true; } void AnalyzerInformation::reportErr(const ErrorLogger::ErrorMessage &msg, bool /*verbose*/) { if (fout.is_open()) fout << msg.toXML() << '\n'; } void AnalyzerInformation::setFileInfo(const std::string &check, const std::string &fileInfo) { if (fout.is_open() && !fileInfo.empty()) fout << " \n" << fileInfo << " \n"; } cppcheck-1.82/lib/analyzerinfo.h000066400000000000000000000045541322667425100166640ustar00rootroot00000000000000/* * 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 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 fout; std::string analyzerInfoFile; }; /// @} //--------------------------------------------------------------------------- #endif // analyzerinfoH cppcheck-1.82/lib/astutils.cpp000066400000000000000000000533241322667425100163650ustar00rootroot00000000000000/* * 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 "astutils.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "valueflow.h" #include 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)); } 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->isName() && !tok->varId() && tok->values().size() == 1U && tok->values().front().isKnown() && 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(); } while (ret && ret->str() == ".") ret = ret->astOperand2(); if (ret && ret->varId() == 0U) ret = nullptr; if (vartok) *vartok = ret; return ret; } bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure) { 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(); } 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) && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure); } 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 (tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof") { if (!tok1->function() && !Token::Match(tok1->previous(), ".|::") && pure && !library.isFunctionConst(tok1->str(), true) && !tok1->isAttributeConst() && !tok1->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 (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; if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName()) { // cast => assert that the casts are equal 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); noncommutativeEquals = noncommutativeEquals && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand2(), library, pure); 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->astOperand2()) { 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->astOperand1() && tok1->astOperand2() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); bool commutativeEquals = commutative && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure); commutativeEquals = commutativeEquals && isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure); return commutativeEquals; } bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const Library& library, bool pure) { 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); if (cond2->astOperand2() && cond2->astOperand2()->str() == "0") return isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure); } return isSameExpression(cpp, true, cond1->astOperand1(), cond2, library, pure); } if (cond2->str() == "!") return isOppositeCond(isNot, cpp, cond2, cond1, library, pure); 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) && isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand2(), library, pure)) { comp2 = cond2->str(); } else if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand2(), library, pure) && isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand1(), library, pure)) { 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)) 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 == "<")))); } bool isConstExpression(const Token *tok, const Library& library, bool pure) { 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; // bailout when we see ({..}) if (tok->str() == "{") return false; return isConstExpression(tok->astOperand1(), library, pure) && isConstExpression(tok->astOperand2(), library, pure); } 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 isReturnScope(const Token * const endToken) { 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) && isReturnScope(prev->link()->tokAt(-2)); if (Token::simpleMatch(prev->link()->previous(), ") {") && Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") && !Token::findsimplematch(prev->link(), "break", prev)) { return true; } if (Token::simpleMatch(prev->link()->previous(), ") {") && Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "return (")) { return true; } if (Token::Match(prev->link()->previous(), "[;{}] {")) return isReturnScope(prev); } else if (Token::simpleMatch(prev, ";")) { // noreturn function if (Token::simpleMatch(prev->previous(), ") ;") && Token::Match(prev->linkAt(-1)->tokAt(-2), "[;{}] %name% (")) return true; // return/goto statement prev = prev->previous(); while (prev && !Token::Match(prev, ";|{|}|return|goto|throw|continue|break")) prev = prev->previous(); return prev && prev->isName(); } return false; } bool isVariableChangedByFunctionCall(const Token *tok, unsigned int varid, const Settings *settings, bool *inconclusive) { if (!tok) return false; if (tok->varId() == varid) return isVariableChangedByFunctionCall(tok, settings, inconclusive); return isVariableChangedByFunctionCall(tok->astOperand1(), varid, settings, inconclusive) || isVariableChangedByFunctionCall(tok->astOperand2(), varid, settings, inconclusive); } bool isVariableChangedByFunctionCall(const Token *tok, const Settings *settings, bool *inconclusive) { if (!tok) return false; // address of variable const bool addressOf = Token::simpleMatch(tok->previous(), "&"); // passing variable to subfunction? if (Token::Match(tok->tokAt(-2), ") & %name% [,)]") && Token::Match(tok->linkAt(-2)->previous(), "[,(] (")) ; else if (Token::Match(tok->tokAt(addressOf?-2:-1), "[(,] &| %name% [,)]")) ; else if (Token::Match(tok->tokAt(addressOf?-2:-1), "[?:] &| %name% [:,)]")) { const Token *parent = tok->astParent(); if (parent == tok->previous() && parent->str() == "&") parent = parent->astParent(); while (Token::Match(parent, "[?:]")) parent = parent->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (!parent || parent->str() != "(") return false; } else return false; // reinterpret_cast etc.. if (Token::Match(tok->tokAt(-3), "> ( & %name% ) [,)]") && tok->linkAt(-3) && Token::Match(tok->linkAt(-3)->tokAt(-2), "[,(] %type% <")) tok = tok->linkAt(-3); // goto start of function call and get argnr unsigned int argnr = 0; while (tok && tok->str() != "(") { if (tok->str() == ",") ++argnr; else if (tok->str() == ")") tok = tok->link(); tok = tok->previous(); } tok = tok ? tok->previous() : nullptr; if (tok && tok->link() && tok->str() == ">") tok = tok->link()->previous(); if (!Token::Match(tok, "%name% [(<]")) return false; // not a function => variable not changed // Constructor call if (tok->variable() && tok->variable()->nameToken() == tok) { // Find constructor.. const unsigned int argCount = numberOfArguments(tok); const ::Scope *typeScope = tok->variable()->typeScope(); if (typeScope) { for (std::list::const_iterator it = typeScope->functionList.begin(); it != typeScope->functionList.end(); ++it) { if (!it->isConstructor() || it->argCount() < argCount) continue; const Variable *arg = it->getArgumentVar(argnr); if (arg && arg->isReference() && !arg->isConst()) return true; } return false; } if (inconclusive) *inconclusive = true; return false; } if (!tok->function()) { // 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->library.isnullargbad(tok, 1+argnr)) return false; // addressOf => inconclusive if (!addressOf) { if (inconclusive != nullptr) *inconclusive = true; return false; } return true; } const Variable *arg = tok->function()->getArgumentVar(argnr); if (addressOf && !(arg && arg->isConst())) return true; return arg && !arg->isConst() && arg->isReference(); } bool isVariableChanged(const Token *start, const Token *end, const unsigned int varid, bool globalvar, const Settings *settings) { for (const 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 true; continue; } if (Token::Match(tok, "%name% %assign%|++|--")) return true; if (Token::Match(tok->previous(), "++|-- %name%")) return true; if (Token::simpleMatch(tok->previous(), ">>")) { const Token *shr = tok->previous(); if (Token::simpleMatch(shr->astParent(), ">>")) return true; const Token *lhs = shr->astOperand1(); if (!lhs || !lhs->valueType() || !lhs->valueType()->isIntegral()) return true; } const Token *ftok = tok; while (ftok && !Token::Match(ftok, "[({[]")) ftok = ftok->astParent(); if (ftok && Token::Match(ftok->link(), ") !!{")) { bool inconclusive = false; bool isChanged = isVariableChangedByFunctionCall(tok, settings, &inconclusive); isChanged |= inconclusive; if (isChanged) return true; } const Token *parent = tok->astParent(); while (Token::Match(parent, ".|::")) parent = parent->astParent(); if (parent && parent->tokType() == Token::eIncDecOp) 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; } static void getArgumentsRecursive(const Token *tok, std::vector *arguments) { if (!tok) return; if (tok->str() == ",") { getArgumentsRecursive(tok->astOperand1(), arguments); getArgumentsRecursive(tok->astOperand2(), arguments); } else { arguments->push_back(tok); } } std::vector getArguments(const Token *ftok) { std::vector arguments; getArgumentsRecursive(ftok->next()->astOperand2(), &arguments); return arguments; } const Token *findLambdaEndToken(const Token *first) { if (!first || first->str() != "[") return nullptr; const Token* tok = first->link(); if (Token::simpleMatch(tok, "] {")) return tok->linkAt(1); if (!Token::simpleMatch(tok, "] (")) return nullptr; tok = tok->linkAt(1)->next(); if (tok && tok->str() == "constexpr") tok = tok->next(); if (tok && tok->str() == "mutable") tok = tok->next(); if (tok && tok->str() == "{") return tok->link(); return nullptr; } cppcheck-1.82/lib/astutils.h000066400000000000000000000110741322667425100160260ustar00rootroot00000000000000/* * 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 astutilsH #define astutilsH //--------------------------------------------------------------------------- #include #include class Library; class Settings; class Token; /** 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); /** * 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 isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure); /** * 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 */ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const Library& library, bool pure); bool isConstExpression(const Token *tok, const Library& library, bool pure); bool isWithoutSideEffects(bool cpp, const Token* tok); /** Is scope a return scope (scope will unconditionally return) */ bool isReturnScope(const Token *endToken); /** 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, unsigned 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, const Settings *settings, bool *inconclusive); /** Is variable changed in block of code? */ bool isVariableChanged(const Token *start, const Token *end, const unsigned int varid, bool globalvar, const Settings *settings); /** 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); /** * find lambda function end token * \todo handle explicit return type * \param first The [ token * \return nullptr or the } */ const Token *findLambdaEndToken(const Token *first); #endif // astutilsH cppcheck-1.82/lib/check.cpp000066400000000000000000000041651322667425100155710ustar00rootroot00000000000000/* * 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 "check.h" #include //--------------------------------------------------------------------------- Check::Check(const std::string &aname) : _tokenizer(nullptr), _settings(nullptr), _errorLogger(nullptr), _name(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 } cppcheck-1.82/lib/check.h000066400000000000000000000156641322667425100152440ustar00rootroot00000000000000/* * 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 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; } /** 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) : _tokenizer(tokenizer), _settings(settings), _errorLogger(errorLogger), _name(aname) { } virtual ~Check() { if (!_tokenizer) 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 *) { } /** run checks, the token list is simplified */ virtual void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *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 _name; } /** 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 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 std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { (void)fileInfo; (void)settings; (void)errorLogger; return false; } protected: const Tokenizer * const _tokenizer; const Settings * const _settings; ErrorLogger * const _errorLogger; /** 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, _tokenizer ? &_tokenizer->list : nullptr, severity, id, msg, cwe, inconclusive); if (_errorLogger) _errorLogger->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, _tokenizer ? &_tokenizer->list : nullptr, severity, id, msg, cwe, inconclusive); if (_errorLogger) _errorLogger->reportErr(errmsg); else reportError(errmsg); } ErrorPath getErrorPath(const Token *errtok, const ValueFlow::Value *value, const std::string &bug) const { ErrorPath errorPath; if (!value) { errorPath.push_back(ErrorPathItem(errtok,bug)); } else if (_settings->verbose || _settings->xml || _settings->outputFormat == "daca2") { errorPath = value->errorPath; errorPath.push_back(ErrorPathItem(errtok,bug)); } else { if (value->condition) errorPath.push_back(ErrorPathItem(value->condition, "condition '" + value->condition->expressionString() + "'")); //else if (!value->isKnown() || value->defaultArg) // errorPath = value->callstack; errorPath.push_back(ErrorPathItem(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); private: const std::string _name; /** disabled assignment operator and copy constructor */ void operator=(const Check &); Check(const Check &); }; /// @} //--------------------------------------------------------------------------- #endif // checkH cppcheck-1.82/lib/check64bit.cpp000066400000000000000000000164651322667425100164500ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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 (!_settings->isEnabled(Settings::PORTABILITY)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Check return values const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; 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->classStart->next(); tok != scope->classEnd; 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 (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; 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.82/lib/check64bit.h000066400000000000000000000062241322667425100161050ustar00rootroot00000000000000/* * 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 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) { Check64BitPortability check64BitPortability(tokenizer, settings, errorLogger); check64BitPortability.pointerassignment(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { (void)tokenizer; (void)settings; (void)errorLogger; } /** 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 { 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 { 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.82/lib/checkassert.cpp000066400000000000000000000133311322667425100170060ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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 (!_settings->isEnabled(Settings::WARNING)) return; for (const Token* tok = _tokenizer->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->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) { if (tok2->tokType() != Token::eAssignmentOp && 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->classStart; rt != scope->classEnd; 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", "Assert statement calls a function which may have desired side effects: '" + functionName + "'.\n" "Non-pure function: '" + functionName + "' 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", "Assert statement modifies '" + varname + "'.\n" "Variable '" + varname + "' is modified insert 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.82/lib/checkassert.h000066400000000000000000000051611322667425100164550ustar00rootroot00000000000000/* * 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 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) { } virtual void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckAssert check(tokenizer, settings, errorLogger); check.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 { CheckAssert c(nullptr, settings, errorLogger); c.sideEffectInAssertError(nullptr, "function"); c.assignmentInAssertError(nullptr, "var"); } static std::string myName() { return "Assert"; } std::string classInfo() const { return "Warn if there are side effects in assert statements (since this cause different behaviour in debug/release builds).\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkassertH cppcheck-1.82/lib/checkautovariables.cpp000066400000000000000000000521741322667425100203560ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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 //--------------------------------------------------------------------------- // 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 bool CheckAutoVariables::isPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isPointer()); } bool CheckAutoVariables::isArrayArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isArray()); } bool CheckAutoVariables::isRefPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isReference() && var->isPointer()); } bool CheckAutoVariables::isNonReferenceArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && !var->isReference() && (var->isPointer() || var->typeStartToken()->isStandardType() || var->type())); } bool CheckAutoVariables::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; } bool CheckAutoVariables::isAutoVarArray(const Token *tok) { if (!tok) return false; // &x[..] if (tok->str() == "&" && Token::simpleMatch(tok->astOperand1(), "[") && !tok->astOperand2()) 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 variableIsUsedInScope(const Token* start, unsigned int varId, const Scope *scope) { if (!start) // Ticket #5024 return false; for (const Token *tok = start; tok && tok != scope->classEnd; 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 = _settings->isEnabled(Settings::STYLE); const bool printWarning = _settings->isEnabled(Settings::WARNING); if (!printStyle && !printWarning) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { // TODO: What happens if this is removed? if (tok->astParent()) continue; if (!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->classEnd)) { if (vartok->variable()->isPointer() && printWarning) errorUselessAssignmentPtrArg(vartok); else if (printStyle) errorUselessAssignmentArg(vartok); } } } } void CheckAutoVariables::autoVariables() { const bool printInconclusive = _settings->inconclusive; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { // 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% = & %var%") && isPtrArg(tok->tokAt(2)) && isAutoVar(tok->tokAt(5))) { if (checkRvalueExpression(tok->tokAt(5))) 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())) { const Token * const var2tok = tok->tokAt(6); if (isAutoVar(var2tok) && checkRvalueExpression(var2tok)) errorAutoVariableAssignment(tok->next(), true); } tok = tok->tokAt(6); } 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::Match(tok->linkAt(2), "] = & %var%") && (isPtrArg(tok->next()) || isArrayArg(tok->next())) && isAutoVar(tok->linkAt(2)->tokAt(3))) { const Token* const varTok = tok->linkAt(2)->tokAt(3); if (checkRvalueExpression(varTok)) errorAutoVariableAssignment(tok->next(), false); } // Critical return else if (Token::Match(tok, "return & %var% ;")) { const Token* varTok = tok->tokAt(2); if (isAutoVar(varTok)) errorReturnAddressToAutoVariable(tok); else if (varTok->varId()) { const Variable * var1 = varTok->variable(); if (var1 && var1->isArgument() && var1->typeEndToken()->str() != "&") errorReturnAddressOfFunctionParameter(tok, varTok->str()); } } // Invalid pointer deallocation else if ((Token::Match(tok, "%name% ( %var% ) ;") && _settings->library.dealloc(tok)) || (_tokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isAutoVarArray(tok)) errorInvalidDeallocation(tok); } else if ((Token::Match(tok, "%name% ( & %var% ) ;") && _settings->library.dealloc(tok)) || (_tokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isAutoVar(tok)) errorInvalidDeallocation(tok); } } } } //--------------------------------------------------------------------------- void CheckAutoVariables::returnPointerToLocalArray() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (!scope->function) continue; const Token *tok = scope->function->tokenDef; // have we reached a function that returns a pointer if (tok->previous() && tok->previous()->str() == "*") { for (const Token *tok2 = scope->classStart->next(); tok2 && tok2 != scope->classEnd; tok2 = tok2->next()) { // Return pointer to local array variable.. if (tok2 ->str() == "return" && isAutoVarArray(tok2->astOperand1())) { errorReturnPointerToLocalArray(tok2); } } } } } void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of an auto-variable 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", "Address of function parameter '" + varname + "' returned.\n" "Address of the function parameter '" + varname + "' 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); } //--------------------------------------------------------------------------- // return temporary? bool CheckAutoVariables::returnTemporary(const Token *tok) { bool func = false; // Might it be a function call? bool retref = false; // is there such a function that returns a reference? bool retvalue = false; // is there such a function that returns a value? const Function *function = tok->function(); if (function) { // Ticket #5478: Only functions or operator equal might return a temporary if (function->type != Function::eOperatorEqual && function->type != Function::eFunction) return false; retref = function->tokenDef->strAt(-1) == "&"; if (!retref) { const Token *start = function->retDef; if (start->str() == "const") start = start->next(); if (start->str() == "::") start = start->next(); if (Token::simpleMatch(start, "std ::")) { if (start->strAt(3) != "<" || !Token::simpleMatch(start->linkAt(3), "> ::")) retvalue = true; else retref = true; // Assume that a reference is returned } else { if (start->type()) retvalue = true; else retref = true; } } func = true; } if (!func && tok->type()) return true; return (!retref && retvalue); } //--------------------------------------------------------------------------- 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->str() == "*" && !tok2->astOperand2()) 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; } void CheckAutoVariables::returnReference() { if (_tokenizer->isC()) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (!scope->function) continue; const Token *tok = scope->function->tokenDef; // have we reached a function that returns a reference? if (tok->previous() && tok->previous()->str() == "&") { for (const Token *tok2 = scope->classStart->next(); tok2 && tok2 != scope->classEnd; tok2 = tok2->next()) { if (!tok2->scope()->isExecutable()) { tok2 = tok2->scope()->classEnd; if (!tok2) break; continue; } // Skip over lambdas const Token *lambdaEndToken = findLambdaEndToken(tok2); if (lambdaEndToken) tok2 = lambdaEndToken->next(); if (tok2->str() == "(") tok2 = tok2->link(); if (tok2->str() != "return") continue; // return.. if (Token::Match(tok2, "return %var% ;")) { // is the returned variable a local variable? if (isAutoVar(tok2->next())) { const Variable *var1 = tok2->next()->variable(); // If reference variable is used, check what it references if (Token::Match(var1->nameToken(), "%var% [=(]")) { const Token *tok3 = var1->nameToken()->tokAt(2); if (!Token::Match(tok3, "%var% [);.]")) continue; // Only report error if variable that is referenced is // a auto variable if (!isAutoVar(tok3)) continue; } // report error.. errorReturnReference(tok2); } } // return reference to temporary.. else if (Token::Match(tok2, "return %name% (") && Token::simpleMatch(tok2->linkAt(2), ") ;")) { if (returnTemporary(tok2->next())) { // report error.. errorReturnTempReference(tok2); } } // Return reference to a literal or the result of a calculation else if (tok2->astOperand1() && (tok2->astOperand1()->isCalculation() || tok2->next()->isLiteral()) && astHasAutoResult(tok2->astOperand1())) { errorReturnTempReference(tok2); } } } } } void CheckAutoVariables::errorReturnReference(const Token *tok) { reportError(tok, Severity::error, "returnReference", "Reference to auto variable returned.", CWE562, false); } void CheckAutoVariables::errorReturnTempReference(const Token *tok) { reportError(tok, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, false); } void CheckAutoVariables::errorInvalidDeallocation(const Token *tok) { reportError(tok, Severity::error, "autovarInvalidDeallocation", "Deallocation of an auto-variable results in undefined behaviour.\n" "The deallocation of an auto-variable results in undefined behaviour. You should only free memory " "that has been allocated dynamically.", CWE590, false); } cppcheck-1.82/lib/checkautovariables.h000066400000000000000000000115541322667425100200200ustar00rootroot00000000000000/* * 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 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) { CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.assignFunctionArg(); checkAutoVariables.returnReference(); } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.autoVariables(); checkAutoVariables.returnPointerToLocalArray(); } /** assign function argument */ void assignFunctionArg(); /** Check auto variables */ void autoVariables(); /** Returning pointer to local array */ void returnPointerToLocalArray(); /** Returning reference to local/temporary variable */ void returnReference(); private: static bool isPtrArg(const Token *tok); static bool isArrayArg(const Token *tok); static bool isRefPtrArg(const Token *tok); static bool isNonReferenceArg(const Token *tok); static bool isAutoVar(const Token *tok); static bool isAutoVarArray(const Token *tok); /** * Returning a temporary object? * @param tok pointing at the "return" token * @return true if a temporary object is returned */ static bool returnTemporary(const Token *tok); void errorReturnAddressToAutoVariable(const Token *tok); void errorReturnPointerToLocalArray(const Token *tok); void errorAutoVariableAssignment(const Token *tok, bool inconclusive); void errorReturnReference(const Token *tok); void errorReturnTempReference(const Token *tok); void errorInvalidDeallocation(const Token *tok); 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 { CheckAutoVariables c(nullptr,settings,errorLogger); c.errorAutoVariableAssignment(nullptr, false); c.errorReturnAddressToAutoVariable(nullptr); c.errorReturnPointerToLocalArray(nullptr); c.errorReturnReference(nullptr); c.errorReturnTempReference(nullptr); c.errorInvalidDeallocation(nullptr); c.errorReturnAddressOfFunctionParameter(nullptr, "parameter"); c.errorUselessAssignmentArg(nullptr); c.errorUselessAssignmentPtrArg(nullptr); } static std::string myName() { return "Auto Variables"; } std::string classInfo() const { 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.82/lib/checkbool.cpp000066400000000000000000000471111322667425100164430ustar00rootroot00000000000000/* * 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 "checkbool.h" #include "astutils.h" #include "errorlogger.h" #include "mathlib.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 { 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")); } static bool isNonBoolStdType(const Variable* var) { return (var && var->typeEndToken()->isStandardType() && !Token::Match(var->typeEndToken(), "bool|_Bool")); } //--------------------------------------------------------------------------- void CheckBool::checkIncrementBoolean() { if (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%var% ++")) { const Variable *var = tok->variable(); if (isBool(var)) 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 (!_settings->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 (!_settings->inconclusive) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "(|.|return|&&|%oror%|throw|, %var% [&|]")) { const Variable *var = tok->next()->variable(); if (isBool(var)) { bitwiseOnBooleanError(tok->next(), var->name(), tok->strAt(2) == "&" ? "&&" : "||"); tok = tok->tokAt(2); } } else if (Token::Match(tok, "[&|] %var% )|.|return|&&|%oror%|throw|,") && (!tok->previous() || !tok->previous()->isExtendedOp() || tok->strAt(-1) == ")" || tok->strAt(-1) == "]")) { const Variable *var = tok->next()->variable(); if (isBool(var)) { bitwiseOnBooleanError(tok->next(), var->name(), tok->str() == "&" ? "&&" : "||"); tok = tok->tokAt(2); } } } } } void CheckBool::bitwiseOnBooleanError(const Token *tok, const std::string &varname, const std::string &op) { reportError(tok, Severity::style, "bitwiseOnBoolean", "Boolean variable '" + varname + "' is used in bitwise operation. Did you mean '" + op + "'?", CWE398, true); } //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolWithInt() { if (!_settings->isEnabled(Settings::WARNING) || !_tokenizer->isCPP()) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { const Token* const left = tok->astOperand1(); const Token* const right = tok->astOperand2(); if (left && right && tok->isComparisonOp()) { 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 (!_settings->isEnabled(Settings::STYLE)) return; if (!_tokenizer->isCPP()) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functionsCount = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functionsCount; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->tokType() != Token::eComparisonOp || 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 (!_settings->experimental) return; if (!_settings->isEnabled(Settings::STYLE)) return; if (!_tokenizer->isCPP()) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->tokType() != Token::eComparisonOp || 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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (tok->str() == "=" && astIsBool(tok->astOperand2())) { const Token *lhs = tok->astOperand1(); while (lhs && (lhs->str() == "." || lhs->str() == "::")) lhs = lhs->astOperand2(); if (!lhs || !lhs->variable() || !lhs->variable()->isPointer()) continue; assignBoolToPointerError(tok); } } } } void CheckBool::assignBoolToPointerError(const Token *tok) { reportError(tok, Severity::error, "assignBoolToPointer", "Boolean value assigned to pointer.", CWE587, false); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolExpressionWithInt() { if (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 (isNonBoolStdType(numTok->variable()) && _tokenizer->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 = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 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->classEnd->tokAt(2)) ? scope->classEnd->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->astOperand1() && tok->astOperand2() && 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 (!_tokenizer->isCPP()) return; if (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (tok->str() == "=" && astIsBool(tok->astOperand2())) { const Token *lhs = tok->astOperand1(); while (lhs && (lhs->str() == "." || lhs->str() == "::")) lhs = lhs->astOperand2(); if (!lhs || !lhs->variable()) continue; const Variable* var = lhs->variable(); if (var && var->isFloatingType() && !var->isArrayOrPointer()) assignBoolToFloatError(tok->next()); } } } } void CheckBool::assignBoolToFloatError(const Token *tok) { reportError(tok, Severity::style, "assignBoolToFloat", "Boolean value assigned to floating point variable.", CWE704, false); } cppcheck-1.82/lib/checkbool.h000066400000000000000000000135041322667425100161070ustar00rootroot00000000000000/* * 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 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) { CheckBool checkBool(tokenizer, settings, errorLogger); // Checks checkBool.checkComparisonOfBoolExpressionWithInt(); checkBool.checkComparisonOfBoolWithInt(); checkBool.checkAssignBoolToFloat(); checkBool.pointerArithBool(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckBool checkBool(tokenizer, settings, errorLogger); // Checks 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); 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 &varname, const std::string &op); void comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1); void pointerArithBoolError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { 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, "varname", "&&"); c.comparisonOfBoolExpressionWithIntError(nullptr, true); c.pointerArithBoolError(nullptr); } static std::string myName() { return "Boolean"; } std::string classInfo() const { 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"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboolH cppcheck-1.82/lib/checkboost.cpp000066400000000000000000000050771322667425100166430ustar00rootroot00000000000000/* * 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 "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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart->next(); tok && tok != scope->classEnd; 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.82/lib/checkboost.h000066400000000000000000000050431322667425100163010ustar00rootroot00000000000000/* * 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 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) { } /** Simplified checks. The token list is simplified. */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { 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 { CheckBoost c(nullptr, settings, errorLogger); c.boostForeachError(nullptr); } static std::string myName() { return "Boost usage"; } std::string classInfo() const { return "Check for invalid usage of Boost:\n" "- container modification during BOOST_FOREACH\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboostH cppcheck-1.82/lib/checkbufferoverrun.cpp000066400000000000000000002573321322667425100204120ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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 #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 CWE398(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 CWE786(786U); // Access of Memory Location Before Start of Buffer static const CWE CWE788(788U); // Access of Memory Location After End of Buffer //--------------------------------------------------------------------------- static void makeArrayIndexOutOfBoundsError(std::ostream& oss, const CheckBufferOverrun::ArrayInfo &arrayInfo, const std::vector &index) { oss << "Array '" << arrayInfo.varname(); for (std::size_t i = 0; i < arrayInfo.num().size(); ++i) oss << "[" << arrayInfo.num(i) << "]"; if (index.size() == 1) oss << "' accessed at index " << index[0] << ", which is"; else { oss << "' index " << arrayInfo.varname(); for (std::size_t i = 0; i < index.size(); ++i) oss << "[" << index[i] << "]"; } oss << " out of bounds."; } void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector &index) { std::ostringstream oss; makeArrayIndexOutOfBoundsError(oss, arrayInfo, index); reportError(tok, Severity::error, "arrayIndexOutOfBounds", oss.str(), CWE788, false); } void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector &index) { bool inconclusive = false; const Token *condition = nullptr; for (std::size_t i = 0; i < index.size(); ++i) { inconclusive |= index[i].isInconclusive(); if (condition == nullptr) condition = index[i].condition; } std::list errorPath; if (_settings->xml || _settings->outputFormat == "daca2") { for (std::size_t i = 0; i < index.size(); ++i) { const ErrorPath &e = getErrorPath(tok, &index[i], ""); for (ErrorPath::const_iterator it = e.begin(); it != e.end(); ++it) { const std::string &info = it->second; if (info.empty()) continue; std::string nr; if (index.size() > 1U) nr = "(" + MathLib::toString(i + 1) + getOrdinalText(i + 1) + " array index) "; errorPath.push_back(ErrorPathItem(it->first, nr + info)); } } errorPath.push_back(ErrorPathItem(tok,"Array index out of bounds")); } else { errorPath.push_back(ErrorPathItem(tok, "Array index out of bounds")); if (condition) errorPath.push_back(ErrorPathItem(condition, "Assuming that condition '" + condition->expressionString() + "' is not redundant")); } if (condition != nullptr) { if (!_settings->isEnabled(Settings::WARNING)) return; std::ostringstream errmsg; errmsg << ValueFlow::eitherTheConditionIsRedundant(condition) << " or the array '" << arrayInfo.varname(); for (std::size_t i = 0; i < arrayInfo.num().size(); ++i) errmsg << "[" << arrayInfo.num(i) << "]"; if (index.size() == 1) errmsg << "' is accessed at index " << index[0].intvalue << ", which is out of bounds."; else { errmsg << "' index " << arrayInfo.varname(); for (std::size_t i = 0; i < index.size(); ++i) errmsg << "[" << index[i].intvalue << "]"; errmsg << " is out of bounds."; } reportError(errorPath, Severity::warning, "arrayIndexOutOfBoundsCond", errmsg.str(), CWE119, inconclusive); } else { std::ostringstream errmsg; errmsg << "Array '" << arrayInfo.varname(); for (std::size_t i = 0; i < arrayInfo.num().size(); ++i) errmsg << "[" << arrayInfo.num(i) << "]"; if (index.size() == 1) errmsg << "' accessed at index " << index[0].intvalue << ", which is out of bounds."; else { errmsg << "' index " << arrayInfo.varname(); for (std::size_t i = 0; i < index.size(); ++i) errmsg << "[" << index[i].intvalue << "]"; errmsg << " out of bounds."; } reportError(errorPath, Severity::error, "arrayIndexOutOfBounds", errmsg.str(), CWE119, inconclusive); } } void CheckBufferOverrun::arrayIndexOutOfBoundsError(const std::list &callstack, const ArrayInfo &arrayInfo, const std::vector &index) { std::ostringstream oss; makeArrayIndexOutOfBoundsError(oss, arrayInfo, index); reportError(callstack, Severity::error, "arrayIndexOutOfBounds", oss.str()); } static std::string bufferOverrunMessage(std::string varnames) { varnames.erase(std::remove(varnames.begin(), varnames.end(), ' '), varnames.end()); std::string errmsg("Buffer is accessed out of bounds"); if (!varnames.empty()) errmsg += ": " + varnames; else errmsg += "."; return errmsg; } void CheckBufferOverrun::bufferOverrunError(const Token *tok, const std::string &varnames) { reportError(tok, Severity::error, "bufferAccessOutOfBounds", bufferOverrunMessage(varnames), CWE788, false); } void CheckBufferOverrun::bufferOverrunError(const std::list &callstack, const std::string &varnames) { reportError(callstack, Severity::error, "bufferAccessOutOfBounds", bufferOverrunMessage(varnames)); } void CheckBufferOverrun::possibleBufferOverrunError(const Token *tok, const std::string &src, const std::string &dst, bool cat) { if (cat) reportError(tok, Severity::warning, "possibleBufferAccessOutOfBounds", "Possible buffer overflow if strlen(" + src + ") is larger than sizeof(" + dst + ")-strlen(" + dst +").\n" "Possible buffer overflow if strlen(" + src + ") is larger than sizeof(" + dst + ")-strlen(" + dst +"). " "The source buffer is larger than the destination buffer so there is the potential for overflowing the destination buffer.", CWE398, false); else reportError(tok, Severity::warning, "possibleBufferAccessOutOfBounds", "Possible buffer overflow if strlen(" + src + ") is larger than or equal to sizeof(" + dst + ").\n" "Possible buffer overflow if strlen(" + src + ") is larger than or equal to sizeof(" + dst + "). " "The source buffer is larger than the destination buffer so there is the potential for overflowing the destination buffer.", CWE398, false); } void CheckBufferOverrun::strncatUsageError(const Token *tok) { if (_settings && !_settings->isEnabled(Settings::WARNING)) return; reportError(tok, Severity::warning, "strncatUsage", "Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n" "At most, strncat appends the 3rd parameter's amount of characters and adds a terminating null byte.\n" "The safe way to use strncat is to subtract one from the remaining space in the buffer and use it as 3rd parameter." "Source: http://www.cplusplus.com/reference/cstring/strncat/\n" "Source: http://www.opensource.apple.com/source/Libc/Libc-167/gen.subproj/i386.subproj/strncat.c", CWE119, false); } void CheckBufferOverrun::outOfBoundsError(const Token *tok, const std::string &what, const bool show_size_info, const MathLib::bigint &supplied_size, const MathLib::bigint &actual_size) { std::ostringstream oss; oss << what << " is out of bounds"; if (show_size_info) oss << ": Supplied size " << supplied_size << " is larger than actual size " << actual_size; oss << '.'; reportError(tok, Severity::error, "outOfBounds", oss.str(), CWE788, false); } void CheckBufferOverrun::pointerOutOfBoundsError(const Token *tok, const Token *index, const MathLib::bigint indexvalue) { // The severity is portability instead of error since this ub doesn't // cause bad behaviour on most implementations. people create out // of bounds pointers by intention. const std::string expr(tok ? tok->expressionString() : std::string()); std::string errmsg; if (index && !index->isNumber()) { errmsg = "Undefined behaviour, when '" + index->expressionString() + "' is " + MathLib::toString(indexvalue) + " the pointer arithmetic '" + expr + "' is out of bounds"; } else { errmsg = "Undefined behaviour, pointer arithmetic '" + expr + "' is out of bounds"; } std::string verbosemsg(errmsg + ". From chapter 6.5.6 in the C specification:\n" "\"When an expression that has integer type is added to or subtracted from a pointer, ..\" and then \"If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.\""); reportError(tok, Severity::portability, "pointerOutOfBounds", errmsg + ".\n" + verbosemsg, CWE398, false); /* "Undefined behaviour: The result of this pointer arithmetic does not point into or just one element past the end of the " + object + ". Further information: https://www.securecoding.cert.org/confluence/display/seccode/ARR30-C.+Do+not+form+or+use+out+of+bounds+pointers+or+array+subscripts"); */ } void CheckBufferOverrun::sizeArgumentAsCharError(const Token *tok) { if (_settings && !_settings->isEnabled(Settings::WARNING)) return; reportError(tok, Severity::warning, "sizeArgumentAsChar", "The size argument is given as a char constant.", CWE682, false); } void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "terminateStrncpy", "The buffer '" + varname + "' may not be null-terminated after the call to strncpy().\n" "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::cmdLineArgsError(const Token *tok) { reportError(tok, Severity::error, "insecureCmdLineArgs", "Buffer overrun possible for long command line arguments.", CWE119, false); } void CheckBufferOverrun::bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function) { const std::string errmsg = "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); } void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname) { reportError(tok, Severity::warning, "argumentSize", "The array '" + varname + "' is too small, the function '" + functionName + "' expects a bigger one.", CWE398, false); } void CheckBufferOverrun::negativeMemoryAllocationSizeError(const Token *tok) { reportError(tok, Severity::error, "negativeMemoryAllocationSize", "Memory allocation size is negative.\n" "Memory allocation size is negative." "Negative allocation size has no specified behaviour.", CWE131, false); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check array usage.. //--------------------------------------------------------------------------- static bool isAddressOf(const Token *tok) { const Token *tok2 = tok->astParent(); while (Token::Match(tok2, "%name%|.|::|[")) tok2 = tok2->astParent(); return tok2 && tok2->str() == "&" && !(tok2->astOperand1() && tok2->astOperand2()); } /** * bailout if variable is used inside if/else/switch block or if there is "break" * @param tok token for "if" or "switch" * @param varid variable id * @return is bailout recommended? */ static bool bailoutIfSwitch(const Token *tok, const unsigned int varid) { const Token* end = tok->linkAt(1)->linkAt(1); if (Token::simpleMatch(end, "} else {")) // scan the else-block end = end->linkAt(2); if (Token::simpleMatch(end, "{")) // Ticket #5203: Invalid code, bailout return true; // Used later to check if the body belongs to a "if" const bool is_if = tok->str() == "if"; for (; tok && tok != end; tok = tok->next()) { // If scanning a "if" block then bailout for "break" if (is_if && (tok->str() == "break" || tok->str() == "continue")) return true; // bailout for "return" else if (tok->str() == "return") return true; // bailout if varid is found else if (tok->varId() == varid) return true; } // No bailout stuff found => return false return false; } //--------------------------------------------------------------------------- static bool checkMinSizes(const std::vector &minsizes, const Token * const ftok, const MathLib::bigint arraySize, const Token **charSizeToken, const Settings * const settings) { if (charSizeToken) *charSizeToken = nullptr; if (minsizes.empty()) return false; // All conditions must be true bool error = true; for (std::vector::const_iterator minsize = minsizes.begin(); minsize != minsizes.end(); ++minsize) { if (!error) return false; error = false; const Token *argtok = ftok->tokAt(2); for (int argnum = 1; argtok && argnum < minsize->arg; argnum++) argtok = argtok->nextArgument(); if (!argtok) return false; switch (minsize->type) { case Library::ArgumentChecks::MinSize::ARGVALUE: if (Token::Match(argtok, "%num% ,|)")) { const MathLib::bigint sz = MathLib::toLongNumber(argtok->str()); if (sz > arraySize) error = true; } else if (argtok->tokType() == Token::eChar && Token::Match(argtok->next(), ",|)") && charSizeToken) *charSizeToken = argtok; //sizeArgumentAsCharError(argtok); break; case Library::ArgumentChecks::MinSize::MUL: // TODO: handle arbitrary arg2 if (minsize->arg2 == minsize->arg+1 && Token::Match(argtok, "%num% , %num% ,|)")) { const MathLib::bigint sz = MathLib::toLongNumber(argtok->str()) * MathLib::toLongNumber(argtok->strAt(2)); if (sz > arraySize) error = true; } break; case Library::ArgumentChecks::MinSize::STRLEN: { if (settings->library.isargformatstr(ftok,minsize->arg)) { std::list parameters; const std::string &formatstr(argtok->str()); const Token *argtok2 = argtok; while ((argtok2 = argtok2->nextArgument()) != nullptr) { if (Token::Match(argtok2, "%num%|%str% [,)]")) parameters.push_back(argtok2); else parameters.push_back(nullptr); } const MathLib::biguint len = CheckBufferOverrun::countSprintfLength(formatstr, parameters); if (len > arraySize + 2) error = true; } else { const Token *strtoken = argtok->getValueTokenMaxStrLength(); if (strtoken && Token::getStrLength(strtoken) >= arraySize) error = true; } } break; case Library::ArgumentChecks::MinSize::SIZEOF: if (argtok->tokType() == Token::eString && Token::getStrLength(argtok) >= arraySize) error = true; break; case Library::ArgumentChecks::MinSize::NONE: return false; }; } return error; } void CheckBufferOverrun::checkFunctionParameter(const Token &ftok, unsigned int paramIndex, const ArrayInfo &arrayInfo, const std::list& callstack) { const std::vector * const minsizes = _settings->library.argminsizes(&ftok, paramIndex); if (minsizes) { MathLib::bigint arraySize = arrayInfo.element_size(); if (arraySize == 0) return; for (std::size_t i = 0; i < arrayInfo.num().size(); ++i) arraySize *= arrayInfo.num(i); // dimension is 0 or unknown => bailout if (arraySize == 0) return; const Token *charSizeToken = nullptr; if (checkMinSizes(*minsizes, &ftok, arraySize, &charSizeToken, _settings)) bufferOverrunError(callstack, arrayInfo.varname()); if (charSizeToken) sizeArgumentAsCharError(charSizeToken); } // Calling a user function? // only 1-dimensional arrays can be checked currently else if (arrayInfo.num().size() == 1) { const Function* const func = ftok.function(); if (func && func->hasBody()) { // Get corresponding parameter.. const Variable* const parameter = func->getArgumentVar(paramIndex-1); // Ensure that it has a compatible size.. if (!parameter || sizeOfType(parameter->typeStartToken()) != arrayInfo.element_size()) return; // No variable id occur for instance when: // - Variable function arguments: "void f(...)" // - Unnamed parameter: "void f(char *)" if (parameter->declarationId() == 0) return; // Check the parameter usage in the function scope.. for (const Token* ftok2 = func->functionScope->classStart; ftok2 != func->functionScope->classEnd; ftok2 = ftok2->next()) { if (Token::Match(ftok2, "if|for|switch|while (")) { // bailout if there is buffer usage.. if (bailoutIfSwitch(ftok2, parameter->declarationId())) { break; } // no bailout is needed. skip the if-block else { // goto end of if block.. ftok2 = ftok2->linkAt(1)->linkAt(1); if (Token::simpleMatch(ftok2, "} else {")) ftok2 = ftok2->linkAt(2); if (!ftok2) break; continue; } } if (ftok2->str() == "}") break; if (ftok2->varId() == parameter->declarationId()) { if (Token::Match(ftok2->previous(), "-- %name%") || Token::Match(ftok2, "%name% --")) break; if (Token::Match(ftok2->previous(), ";|{|}|%op% %name% [ %num% ]")) { const MathLib::bigint index = MathLib::toLongNumber(ftok2->strAt(2)); if (index >= 0 && arrayInfo.num(0) > 0 && index >= arrayInfo.num(0)) { std::list callstack2(callstack); callstack2.push_back(ftok2); std::vector indexes(1, index); arrayIndexOutOfBoundsError(callstack2, arrayInfo, indexes); } } } // Calling function.. if (Token::Match(ftok2, "%name% (")) { ArrayInfo ai(arrayInfo); ai.declarationId(parameter->declarationId()); checkFunctionCall(ftok2, ai, callstack); } } } } // Check 'float x[10]' arguments in declaration if (_settings->isEnabled(Settings::WARNING)) { const Function* const func = ftok.function(); // If argument is '%type% a[num]' then check bounds against num if (func) { const Variable* const argument = func->getArgumentVar(paramIndex-1); const Token *nameToken; if (argument && Token::Match(argument->typeStartToken(), "%type% %var% [ %num% ] [,)[]") && (nameToken = argument->nameToken()) != nullptr) { const Token *tok2 = nameToken->next(); MathLib::bigint argsize = sizeOfType(argument->typeStartToken()); if (argsize == 100) // unknown size argsize = 0; do { argsize *= MathLib::toLongNumber(tok2->strAt(1)); tok2 = tok2->tokAt(3); } while (Token::Match(tok2, "[ %num% ] [,)[]")); MathLib::bigint arraysize = arrayInfo.element_size(); if (arraysize == 100) // unknown size arraysize = 0; for (std::size_t i = 0; i < arrayInfo.num().size(); i++) arraysize *= arrayInfo.num(i); if (Token::Match(tok2, "[,)]") && arraysize > 0 && argsize > arraysize) argumentSizeError(&ftok, ftok.str(), arrayInfo.varname()); } } } } void CheckBufferOverrun::checkFunctionCall(const Token *tok, const ArrayInfo &arrayInfo, std::list callstack) { // Don't go deeper than 2 levels, the checking can get very slow // when there is no limit if (callstack.size() >= 2) return; // Prevent recursion for (std::list::const_iterator it = callstack.cbegin(); it != callstack.cend(); ++it) { // Same function name => bail out if (tok->str() == (*it)->str()) return; } callstack.push_back(tok); const unsigned int declarationId = arrayInfo.declarationId(); const Token *argtok = tok->tokAt(2); unsigned int argnr = 1U; while (argtok) { if (Token::Match(argtok, "%varid% ,|)", declarationId)) checkFunctionParameter(*tok, argnr, arrayInfo, callstack); else if (Token::Match(argtok, "%varid% + %num% ,|)", declarationId)) { const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(argtok->strAt(2)))); checkFunctionParameter(*tok, argnr, ai, callstack); } // goto next parameter.. argtok = argtok->nextArgument(); argnr++; } } void CheckBufferOverrun::checkScope(const Token *tok, const std::vector &varname, const ArrayInfo &arrayInfo) { const MathLib::bigint size = arrayInfo.num(0); if (size <= 0) // unknown size return; if (tok->str() == "return") { tok = tok->next(); if (!tok) return; } const bool printInconclusive = _settings->inconclusive; const MathLib::bigint total_size = arrayInfo.element_size() * size; const unsigned int declarationId = arrayInfo.declarationId(); std::string varnames; for (std::size_t i = 0; i < varname.size(); ++i) varnames += (i == 0 ? "" : " . ") + *varname[i]; const int varcount = varname.empty() ? 0 : static_cast((varname.size() - 1) * 2U); // ValueFlow array index.. if ((declarationId > 0 && Token::Match(tok, "%varid% [", declarationId)) || (declarationId == 0 && Token::simpleMatch(tok, (varnames + " [").c_str()))) { const Token *tok2 = tok->next(); while (tok2->str() != "[") tok2 = tok2->next(); valueFlowCheckArrayIndex(tok2, arrayInfo); } // If the result of pointer arithmetic means that the pointer is // out of bounds then this flag will be set. bool pointerIsOutOfBounds = false; const bool printPortability = _settings->isEnabled(Settings::PORTABILITY); for (const Token* const end = tok->scope()->classEnd; tok && tok != end; tok = tok->next()) { if (declarationId != 0 && Token::Match(tok, "%varid% = new|malloc|realloc", declarationId)) { // Abort break; } // reassign buffer if (declarationId > 0 && Token::Match(tok, "[;{}] %varid% = %any%", declarationId)) { // using varid .. bailout if (tok->tokAt(3)->varId() != declarationId) break; pointerIsOutOfBounds = false; } // Array index.. if ((declarationId > 0 && ((tok->str() == "return" || (!tok->isName() && !Token::Match(tok, "[.&]"))) && Token::Match(tok->next(), "%varid% [", declarationId))) || (declarationId == 0 && ((tok->str() == "return" || (!tok->isName() && !Token::Match(tok, "[.&]"))) && (Token::Match(tok->next(), (varnames + " [").c_str()) || Token::Match(tok->next(), (*varname[0] +" [ %num% ] . " + *varname[1] + " [ %num% ]").c_str()))))) { std::vector indexes; const Token *tok2 = tok->tokAt(2 + varcount); for (; Token::Match(tok2, "[ %num% ]"); tok2 = tok2->tokAt(3)) { const MathLib::bigint index = MathLib::toLongNumber(tok2->strAt(1)); indexes.push_back(index); } for (; Token::Match(tok2->tokAt(3), "[ %num% ]"); tok2 = tok2->tokAt(3)) { const MathLib::bigint index = MathLib::toLongNumber(tok2->strAt(4)); indexes.push_back(index); } if (indexes.empty() && arrayInfo.num().size() == 1U && Token::simpleMatch(tok2, "[") && tok2->astOperand2()) { const ValueFlow::Value *value = tok2->astOperand2()->getMaxValue(false); if (value) { indexes.push_back(value->intvalue); } } if (indexes.size() == arrayInfo.num().size()) { // Check if the indexes point outside the whole array.. // char a[10][10]; // a[0][20] <-- ok. // a[9][20] <-- error. // total number of elements of array.. MathLib::bigint totalElements = 1; // total index.. MathLib::bigint totalIndex = 0; // calculate the totalElements and totalIndex.. for (std::size_t i = 0; i < indexes.size(); ++i) { const std::size_t ri = indexes.size() - 1 - i; totalIndex += indexes[ri] * totalElements; totalElements *= arrayInfo.num(ri); if (arrayInfo.num(ri) == -1) { // unknown size totalElements = 0; break; } } // totalElements == 0 => Unknown size if (totalElements == 0) continue; const Token *tok3 = tok->previous(); while (tok3 && Token::Match(tok3->previous(), "%name% .")) tok3 = tok3->tokAt(-2); // taking address of 1 past end? if (totalIndex == totalElements) { const bool addr = (tok3 && (tok3->str() == "&" || Token::simpleMatch(tok3->previous(), "& ("))); if (addr) continue; } // Is totalIndex in bounds? if (totalIndex > totalElements || totalIndex < 0) { arrayIndexOutOfBoundsError(tok->tokAt(1 + varcount), arrayInfo, indexes); } // Is any array index out of bounds? else { // check each index for overflow for (std::size_t i = 0; i < indexes.size(); ++i) { if (indexes[i] >= arrayInfo.num(i)) { if (indexes.size() == 1U) { arrayIndexOutOfBoundsError(tok->tokAt(1 + varcount), arrayInfo, indexes); break; // only warn about the first one } // The access is still within the memory range for the array // so it may be intentional. else if (printInconclusive) { arrayIndexOutOfBoundsError(tok->tokAt(1 + varcount), arrayInfo, indexes); break; // only warn about the first one } } } } } tok = tok2; continue; } // memset, memcmp, memcpy, strncpy, fgets.. if (declarationId == 0 && Token::Match(tok, "%name% ( !!)")) { std::list callstack; callstack.push_back(tok); const Token* tok2 = tok->tokAt(2); if (Token::Match(tok2, (varnames + " ,").c_str())) checkFunctionParameter(*tok, 1, arrayInfo, callstack); tok2 = tok2->nextArgument(); if (Token::Match(tok2, (varnames + " ,").c_str())) checkFunctionParameter(*tok, 2, arrayInfo, callstack); } if (total_size > 0) { // Writing data into array.. if ((declarationId > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %str%|%var% )", declarationId)) || (declarationId == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %str%|%var% )").c_str()))) { const Token* lastParamTok = tok->tokAt(varcount + 4); if (lastParamTok->tokType() == Token::Type::eString) { const std::size_t len = Token::getStrLength(lastParamTok); if (len >= total_size) { bufferOverrunError(tok, declarationId > 0 ? emptyString : varnames); continue; } } else { const Variable *var = lastParamTok->variable(); if (var && var->isArray() && var->dimensions().size() == 1) { const MathLib::bigint len = var->dimension(0); if (len > total_size) { if (printInconclusive) possibleBufferOverrunError(tok, tok->strAt(4), tok->strAt(2), tok->str() == "strcat"); continue; } } } } // Detect few strcat() calls const std::string strcatPattern = declarationId > 0 ? std::string("strcat ( %varid% , %str% ) ;") : ("strcat ( " + varnames + " , %str% ) ;"); if (Token::Match(tok, strcatPattern.c_str(), declarationId)) { std::size_t charactersAppend = 0; const Token *tok2 = tok; do { charactersAppend += Token::getStrLength(tok2->tokAt(4 + varcount)); if (charactersAppend >= static_cast(total_size)) { bufferOverrunError(tok2); break; } tok2 = tok2->tokAt(7 + varcount); } while (Token::Match(tok2, strcatPattern.c_str(), declarationId)); } // Check function call.. if (Token::Match(tok, "%name% (")) { // No varid => function calls are not handled if (declarationId == 0) continue; const ArrayInfo arrayInfo1(declarationId, varnames, total_size / size, size); const std::list callstack; checkFunctionCall(tok, arrayInfo1, callstack); } } // undefined behaviour: result of pointer arithmetic is out of bounds if (declarationId && Token::Match(tok, "= %varid% + %num% ;", declarationId)) { const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3)); if (printPortability && index > size) pointerOutOfBoundsError(tok->tokAt(2)); if (index >= size && Token::Match(tok->tokAt(-2), "[;{}] %varid% =", declarationId)) pointerIsOutOfBounds = true; } else if (pointerIsOutOfBounds && Token::Match(tok, "[;{}=] * %varid% [;=]", declarationId)) { outOfBoundsError(tok->tokAt(2), tok->strAt(2), false, 0, 0); } } } static std::vector valueFlowGetArrayIndexes(const Token * const tok, bool conditional, std::size_t dimensions) { unsigned int indexvarid = 0; const std::vector empty; std::vector indexes; for (const Token *tok2 = tok; indexes.size() < dimensions && Token::simpleMatch(tok2, "["); tok2 = tok2->link()->next()) { if (!tok2->astOperand2()) return empty; const ValueFlow::Value *index = tok2->astOperand2()->getMaxValue(conditional); if (!index) return empty; if (indexvarid == 0U) indexvarid = index->varId; if (index->varId > 0 && indexvarid != index->varId) return empty; if (index->intvalue < 0) return empty; indexes.push_back(*index); } return indexes; } void CheckBufferOverrun::valueFlowCheckArrayIndex(const Token * const tok, const ArrayInfo &arrayInfo) { // Declaration in global scope or namespace? if (tok->scope()->type == Scope::eGlobal || tok->scope()->type == Scope::eNamespace) return; /* { const Token *parent = tok->astParent(); while (Token::Match(parent, "%name%|::|*|&")) parent = parent->astParent(); if (parent && !Token::simpleMatch(parent, "=")) return; } */ const bool printInconclusive = _settings->inconclusive; // Taking address? const bool addressOf = isAddressOf(tok); // Look for errors first for (int warn = 0; warn == 0 || warn == 1; ++warn) { // Negative index.. for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) { const Token *index = tok2->astOperand2(); if (!index) continue; const ValueFlow::Value *value = index->getValueLE(-1LL,_settings); if (value) negativeIndexError(index, *value); } // Index out of bounds.. const std::vector indexes(valueFlowGetArrayIndexes(tok, warn==1, arrayInfo.num().size())); if (indexes.size() != arrayInfo.num().size()) continue; // Check if the indexes point outside the whole array.. // char a[10][10]; // a[0][20] <-- ok. // a[9][20] <-- error. // total number of elements of array.. const MathLib::bigint totalElements = arrayInfo.numberOfElements(); // total index.. const MathLib::bigint totalIndex = arrayInfo.totalIndex(indexes); // totalElements <= 0 => Unknown size if (totalElements <= 0) continue; if (addressOf && totalIndex == totalElements) continue; // Is totalIndex in bounds? if (totalIndex >= totalElements) { arrayIndexOutOfBoundsError(tok, arrayInfo, indexes); break; } // Is any array index out of bounds? if (printInconclusive) { // check each index for overflow for (std::size_t i = 0; i < indexes.size(); ++i) { if (indexes[i].intvalue >= arrayInfo.num(i)) { // The access is still within the memory range for the array // so it may be intentional. arrayIndexOutOfBoundsError(tok, arrayInfo, indexes); break; // only warn about the first one } } } } } void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo) { bool reassigned = false; for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) { if (reassigned && tok->str() == ";") break; if (tok->varId() != arrayInfo.declarationId()) continue; if (tok->strAt(1) == "=") { reassigned = true; } checkScope_inner(tok, arrayInfo); } } void CheckBufferOverrun::checkScope(const Token *tok, std::map arrayInfos) { unsigned int reassigned = 0; for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) { if (reassigned && tok->str() == ";") { arrayInfos.erase(reassigned); reassigned = 0; } if (!tok->variable() || tok->variable()->nameToken() == tok) continue; std::map::const_iterator arrayInfo = arrayInfos.find(tok->varId()); if (arrayInfo == arrayInfos.cend()) continue; if (tok->strAt(1) == "=") { reassigned = tok->varId(); } checkScope_inner(tok, arrayInfo->second); } } void CheckBufferOverrun::checkScope_inner(const Token *tok, const ArrayInfo &arrayInfo) { const bool printPortability = _settings->isEnabled(Settings::PORTABILITY); const bool printWarning = _settings->isEnabled(Settings::WARNING); const bool printInconclusive = _settings->inconclusive; if (tok->strAt(1) == "[") { valueFlowCheckArrayIndex(tok->next(), arrayInfo); } else if (printPortability && !tok->isCast() && tok->astParent() && tok->astParent()->str() == "+") { // undefined behaviour: result of pointer arithmetic is out of bounds const Token *index; if (tok == tok->astParent()->astOperand1()) index = tok->astParent()->astOperand2(); else index = tok->astParent()->astOperand1(); if (index) { const ValueFlow::Value *value = index->getValueGE(arrayInfo.num(0) + 1U, _settings); if (!value) value = index->getValueLE(-1, _settings); if (value) pointerOutOfBoundsError(tok->astParent(), index, value->intvalue); } } else if (printPortability && tok->astParent() && tok->astParent()->str() == "-") { const Variable *var = symbolDatabase->getVariableFromVarId(arrayInfo.declarationId()); if (var && var->isArray()) { const Token *index = tok->astParent()->astOperand2(); const ValueFlow::Value *value = index ? index->getValueGE(1,_settings) : nullptr; if (index && !value) value = index->getValueLE(-1 - arrayInfo.num(0), _settings); if (value) pointerOutOfBoundsError(tok->astParent(), index, value->intvalue); } } if (!tok->scope()->isExecutable()) // No executable code outside of executable scope - continue to increase performance return; const Token* tok2 = tok->astParent(); if (tok2) { while (tok2->astParent() && !Token::Match(tok2->astParent(), "[,(]")) tok2 = tok2->astParent(); while (tok2->astParent() && tok2->astParent()->str() == ",") tok2 = tok2->astParent(); if (tok2->astParent() && tok2->astParent()->str() == "(") tok2 = tok2->astParent(); if (tok2->str() != "(") return; tok2 = tok2->previous(); // Check function call.. checkFunctionCall(tok2, arrayInfo, std::list()); const MathLib::biguint total_size = arrayInfo.num(0) * arrayInfo.element_size(); if (printWarning && printInconclusive && Token::Match(tok2, "strncpy|memcpy|memmove ( %varid% , %str% , %num% )", arrayInfo.declarationId())) { if (Token::getStrLength(tok2->tokAt(4)) >= total_size) { const MathLib::biguint num = MathLib::toULongNumber(tok2->strAt(6)); if (total_size == num) bufferNotZeroTerminatedError(tok2, tok2->strAt(2), tok2->str()); } } if (printWarning && Token::Match(tok2, "strncpy|strncat ( %varid% ,", arrayInfo.declarationId()) && Token::Match(tok2->linkAt(1)->tokAt(-2), ", %num% )")) { const Token* param3 = tok2->linkAt(1)->previous(); // check for strncpy which is not terminated if (tok2->str() == "strncpy") { // strncpy takes entire variable length as input size const MathLib::biguint num = MathLib::toULongNumber(param3->str()); // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 if (printInconclusive && num >= total_size) { const Token *tok4 = tok2->next()->link()->next(); for (; tok4; tok4 = tok4->next()) { const Token* tok3 = tok2->tokAt(2); if (tok4->varId() == tok3->varId()) { const Token *eq = nullptr; if (Token::Match(tok4, "%varid% [", tok3->varId()) && Token::simpleMatch(tok4->linkAt(1), "] =")) eq = tok4->linkAt(1)->next(); const Token *rhs = eq ? eq->astOperand2() : nullptr; if (!(rhs && rhs->hasKnownIntValue() && rhs->getValue(0))) terminateStrncpyError(tok2, tok3->str()); break; } } } } // Dangerous usage of strncat.. else if (tok2->str() == "strncat") { const MathLib::biguint n = MathLib::toULongNumber(param3->str()); if (n >= total_size) strncatUsageError(tok2); } // Dangerous usage of strncpy + strncat.. if (Token::Match(param3->tokAt(2), "; strncat ( %varid% ,", arrayInfo.declarationId()) && Token::Match(param3->linkAt(4)->tokAt(-2), ", %num% )")) { const MathLib::biguint n = MathLib::toULongNumber(param3->str()) + MathLib::toULongNumber(param3->linkAt(4)->strAt(-1)); if (n > total_size) strncatUsageError(param3->tokAt(3)); } } // Writing data into array.. if (total_size > 0) { if (Token::Match(tok2, "strcpy ( %varid% , %str% )", arrayInfo.declarationId())) { const std::size_t len = Token::getStrLength(tok2->tokAt(4)); if (len >= total_size) { bufferOverrunError(tok2, arrayInfo.varname()); return; } } // Detect few strcat() calls MathLib::biguint charactersAppend = 0; const Token *tok3 = tok2; while (Token::Match(tok3, "strcat ( %varid% , %str% )", arrayInfo.declarationId())) { charactersAppend += Token::getStrLength(tok3->tokAt(4)); if (charactersAppend >= total_size) { bufferOverrunError(tok3, arrayInfo.varname()); break; } tok3 = tok3->tokAt(7); } } } } //--------------------------------------------------------------------------- // Negative size in array declarations //--------------------------------------------------------------------------- static bool isVLAIndex(const Token *index) { std::stack tokens; tokens.push(index); while (!tokens.empty()) { const Token *tok = tokens.top(); tokens.pop(); if (!tok) continue; if (tok->varId() != 0U) return true; if (tok->str() == "?") { // this is a VLA index if both expressions around the ":" is VLA index if (tok->astOperand2() && tok->astOperand2()->str() == ":" && isVLAIndex(tok->astOperand2()->astOperand1()) && isVLAIndex(tok->astOperand2()->astOperand2())) return true; continue; } tokens.push(tok->astOperand1()); tokens.push(tok->astOperand2()); } return false; } void CheckBufferOverrun::negativeArraySize() { for (unsigned int i = 1; i <= _tokenizer->varIdCount(); i++) { const Variable * const var = symbolDatabase->getVariableFromVarId(i); if (!var || !var->isArray()) continue; const Token * const nameToken = var->nameToken(); if (!Token::Match(nameToken, "%var% [") || !nameToken->next()->astOperand2()) continue; const ValueFlow::Value *sz = nameToken->next()->astOperand2()->getValueLE(-1,_settings); // don't warn about constant negative index because that is a compiler error if (sz && isVLAIndex(nameToken->next()->astOperand2())) negativeArraySizeError(nameToken); } } void CheckBufferOverrun::negativeArraySizeError(const Token *tok) { reportError(tok, Severity::error, "negativeArraySize", "Declaration of array '" + (tok ? tok->str() : std::string()) + "' with negative size is undefined behaviour", CWE758, false); } //--------------------------------------------------------------------------- // Checking member variables of structs. //--------------------------------------------------------------------------- bool CheckBufferOverrun::isArrayOfStruct(const Token* tok, int &position) { if (Token::Match(tok->next(), "%name% [ %num% ]")) { tok = tok->tokAt(4); int i = 1; for (;;) { if (Token::Match(tok->next(), "[ %num% ]")) { i++; tok = tok->tokAt(4); } else break; } if (Token::simpleMatch(tok->next(),";")) { position = i; return true; } } return false; } //--------------------------------------------------------------------------- // Checking local variables in a scope //--------------------------------------------------------------------------- void CheckBufferOverrun::checkGlobalAndLocalVariable() { // check string literals for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%str% [") && tok->next()->astOperand2()) { const std::size_t size = Token::getStrSize(tok); const ValueFlow::Value *value = tok->next()->astOperand2()->getMaxValue(false); if (value && value->intvalue >= (isAddressOf(tok) ? size + 1U : size)) bufferOverrunError(tok, tok->str()); } if (Token::Match(tok, "%var% [") && tok->next()->astOperand2() && tok->variable() && tok->variable()->isPointer()) { const ValueFlow::Value *value = tok->next()->astOperand2()->getMaxValue(false); if (!value) continue; for (std::list::const_iterator it = tok->values().begin(); it != tok->values().end(); ++it) { if (!it->isTokValue() || !it->tokvalue) continue; const Variable *var = it->tokvalue->variable(); if (var && var->isArray()) { if (astCanonicalType(tok) != astCanonicalType(it->tokvalue)) continue; const ArrayInfo arrayInfo(var, symbolDatabase); const MathLib::bigint elements = arrayInfo.numberOfElements(); if (elements <= 0) // unknown size continue; const std::vector indexes(valueFlowGetArrayIndexes(tok->next(), false, var->dimensions().size())); if (indexes.size() != var->dimensions().size()) continue; const MathLib::bigint index = arrayInfo.totalIndex(indexes); if (index < (isAddressOf(tok) ? elements + 1U : elements)) continue; std::list callstack; callstack.push_back(it->tokvalue); callstack.push_back(tok); std::vector indexes2(indexes.size()); for (unsigned int i = 0; i < indexes.size(); ++i) indexes2[i] = indexes[i].intvalue; arrayIndexOutOfBoundsError(callstack, arrayInfo, indexes2); } } } } // check all known fixed size arrays first by just looking them up for (std::list::const_iterator scope = symbolDatabase->scopeList.cbegin(); scope != symbolDatabase->scopeList.cend(); ++scope) { std::map arrayInfos; for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { if (!var->isArray() || var->dimension(0) <= 0) continue; _errorLogger->reportProgress(_tokenizer->list.getSourceFilePath(), "Check (BufferOverrun::checkGlobalAndLocalVariable 1)", var->nameToken()->progressValue()); if (_tokenizer->isMaxTime()) return; const Token *tok = var->nameToken(); do { if (tok->str() == "{") { if (Token::simpleMatch(tok->previous(), "= {")) tok = tok->link(); else break; } tok = tok->next(); } while (tok && tok->str() != ";"); if (!tok) break; arrayInfos[var->declarationId()] = ArrayInfo(&*var, symbolDatabase, var->declarationId()); } if (!arrayInfos.empty()) checkScope(scope->classStart ? scope->classStart : _tokenizer->tokens(), arrayInfos); } const std::vector v; // find all dynamically allocated arrays next const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (!Token::Match(tok, "[*;{}] %var% =")) continue; // size : Max array index MathLib::bigint size = 0; // nextTok : used to skip to next statement. const Token * nextTok = tok; _errorLogger->reportProgress(_tokenizer->list.getSourceFilePath(), "Check (BufferOverrun::checkGlobalAndLocalVariable 2)", tok->progressValue()); if (_tokenizer->isMaxTime()) return; // varid : The variable id for the array const Variable *var = tok->next()->variable(); if (_tokenizer->isCPP() && Token::Match(tok, "[*;{}] %var% = new %type% [")) { tok = tok->tokAt(5); if (tok->astOperand2() == nullptr || tok->astOperand2()->getMaxValue(false) == nullptr) continue; size = tok->astOperand2()->getMaxValue(false)->intvalue; nextTok = tok->link()->next(); if (size < 0) { negativeMemoryAllocationSizeError(tok); } } else if (_tokenizer->isCPP() && Token::Match(tok, "[*;{}] %var% = new %type% (|;")) { size = 1; tok = tok->tokAt(5); if (tok->str() == ";") nextTok = tok->next(); else nextTok = tok->link()->next(); } else if (Token::Match(tok, "[*;{}] %var% = malloc|alloca (") && Token::simpleMatch(tok->linkAt(4), ") ;")) { tok = tok->tokAt(4); if (tok->astOperand2() == nullptr || tok->astOperand2()->getMaxValue(false) == nullptr) continue; size = tok->astOperand2()->getMaxValue(false)->intvalue; nextTok = tok->link()->tokAt(2); if (size < 0) { negativeMemoryAllocationSizeError(tok); } /** @todo false negatives: this may be too conservative */ if (!var || !var->isPointer() || var->typeStartToken()->next() != var->typeEndToken()) continue; // malloc() gets count of bytes and not count of // elements, so we should calculate count of elements // manually const unsigned int typeSize = sizeOfType(var->typeStartToken()); if (typeSize > 0) { size /= static_cast(typeSize); } if (size < 0) { negativeMemoryAllocationSizeError(tok); } } else { continue; } if (var == nullptr) continue; const MathLib::bigint totalSize = size * static_cast(sizeOfType(var->typeStartToken())); if (totalSize == 0) continue; ArrayInfo temp(var->declarationId(), var->name(), totalSize / size, size); checkScope(nextTok, v, temp); } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Checking member variables of structs. //--------------------------------------------------------------------------- void CheckBufferOverrun::checkStructVariable() { // find every class and struct const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; // check all variables to see if they are arrays std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { if (var->isArray()) { // create ArrayInfo from the array variable ArrayInfo arrayInfo(&*var, symbolDatabase); // find every function const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t j = 0; j < functions; ++j) { const Scope * func_scope = symbolDatabase->functionScopes[j]; // If struct is declared in a function then check // if scope_func matches if (scope->nestedIn->type == Scope::eFunction && scope->nestedIn != func_scope) { continue; } // check for member variables if (func_scope->functionOf == scope) { // only check non-empty function if (func_scope->classStart->next() != func_scope->classEnd) { // start checking after the { const Token *tok = func_scope->classStart->next(); checkScope(tok, arrayInfo); } } // skip inner scopes.. /** @todo false negatives: handle inner scopes someday */ if (scope->nestedIn->isClassOrStruct()) continue; std::vector varname(2, nullptr); varname[1] = &arrayInfo.varname(); // search the function and it's parameters for (const Token *tok3 = func_scope->classDef; tok3 && tok3 != func_scope->classEnd; tok3 = tok3->next()) { // search for the class/struct name if (tok3->str() != scope->className) continue; // find all array variables int posOfSemicolon = -1; // Declare variable: Fred fred1; if (Token::Match(tok3->next(), "%var% ;")) varname[0] = &tok3->strAt(1); else if (isArrayOfStruct(tok3,posOfSemicolon)) varname[0] = &tok3->strAt(1); // Declare pointer or reference: Fred *fred1 else if (Token::Match(tok3->next(), "*|& %var% [,);=]")) varname[0] = &tok3->strAt(2); else continue; // check for variable sized structure if (scope->type == Scope::eStruct && var->isPublic()) { // last member of a struct with array size of 0 or 1 could be a variable sized structure if (var->dimensions().size() == 1 && var->dimension(0) < 2 && var->index() == (scope->varlist.size() - 1)) { // dynamically allocated so could be variable sized structure if (tok3->next()->str() == "*") { // check for allocation if ((Token::Match(tok3->tokAt(3), "; %name% = malloc ( %num% ) ;") || (Token::Match(tok3->tokAt(3), "; %name% = (") && Token::Match(tok3->linkAt(6), ") malloc ( %num% ) ;"))) && (tok3->strAt(4) == tok3->strAt(2))) { MathLib::bigint size; // find size of allocation if (tok3->strAt(3) == "(") // has cast size = MathLib::toLongNumber(tok3->linkAt(6)->strAt(3)); else size = MathLib::toLongNumber(tok3->strAt(8)); // We don't calculate the size of a structure even when we know // the size of the members. We just assign a length of 100 for // any struct. If the size is less than 100, we assume the // programmer knew the size and specified it rather than using // sizeof(struct). If the size is greater than 100, we assume // the programmer specified the size as sizeof(struct) + number. // Either way, this is just a guess and could be wrong. The // information to make the right decision has been simplified // away by the time we get here. if (size != 100) { // magic number for size of struct // check if a real size was specified and give up // malloc(10) rather than malloc(sizeof(struct)) if (size < 100 || arrayInfo.element_size() == 0) continue; // calculate real array size based on allocated size MathLib::bigint elements = (size - 100) / arrayInfo.element_size(); arrayInfo.num(0, arrayInfo.num(0) + elements); } } // size unknown so assume it is a variable sized structure else continue; } } } // Goto end of statement. const Token *checkTok = nullptr; while (tok3 && tok3 != func_scope->classEnd) { // End of statement. if (tok3->str() == ";") { checkTok = tok3; break; } // End of function declaration.. if (Token::simpleMatch(tok3, ") ;")) break; // Function implementation.. if (Token::simpleMatch(tok3, ") {")) { checkTok = tok3->tokAt(2); break; } tok3 = tok3->next(); } if (!tok3) break; if (!checkTok) continue; // Check variable usage.. ArrayInfo temp = arrayInfo; temp.declarationId(0); // do variable lookup by variable and member names rather than varid std::string varnames; // use class and member name for messages for (std::size_t k = 0; k < varname.size(); ++k) varnames += (k == 0 ? "" : ".") + *varname[k]; temp.varname(varnames); checkScope(checkTok, varname, temp); } } } } } } //--------------------------------------------------------------------------- void CheckBufferOverrun::bufferOverrun() { // singlepass checking using ast, symboldatabase and valueflow for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Array index if (!Token::Match(tok, "%name% [")) continue; // TODO: what to do about negative index.. const Token *index = tok->next()->astOperand2(); if (index && index->getValueLE(-1LL,_settings)) continue; // Set full varname.. std::string varname; if (tok->astParent() && tok->astParent()->str() == ".") { const Token *parent = tok->astParent(); while (parent->astParent() && parent->astParent()->str() == ".") parent = parent->astParent(); varname = parent->expressionString(); } else varname = tok->str(); const Variable * const var = tok->variable(); if (!var) continue; const Token * const strtoken = tok->getValueTokenMinStrSize(); if (strtoken && !var->isArray()) { // TODO: check for access to symbol inside the array bounds, but outside the stored string: // char arr[10] = "123"; // arr[7] = 'x'; // warning: arr[7] is inside the array bounds, but past the string's end ArrayInfo arrayInfo(tok->varId(), varname, 1U, Token::getStrSize(strtoken)); valueFlowCheckArrayIndex(tok->next(), arrayInfo); } else { if (var->nameToken() == tok || !var->isArray()) continue; // unknown array dimensions bool known = true; for (unsigned int i = 0; i < var->dimensions().size(); ++i) { known &= (var->dimension(i) >= 1); known &= var->dimensionKnown(i); } if (!known) continue; // TODO: last array in struct.. if (var->dimension(0) <= 1 && Token::simpleMatch(var->nameToken()->linkAt(1),"] ; }")) continue; if (var->scope() && var->scope()->type == Scope::eUnion) continue; ArrayInfo arrayInfo(var, symbolDatabase); arrayInfo.varname(varname); valueFlowCheckArrayIndex(tok->next(), arrayInfo); } } } //--------------------------------------------------------------------------- MathLib::biguint CheckBufferOverrun::countSprintfLength(const std::string &input_string, const std::list ¶meters) { bool percentCharFound = false; std::size_t input_string_size = 1; bool handleNextParameter = false; std::string digits_string; bool i_d_x_f_found = false; std::list::const_iterator paramIter = parameters.begin(); std::size_t parameterLength = 0; for (std::string::size_type i = 0; i < input_string.length(); ++i) { if (input_string[i] == '\\') { if (i < input_string.length() - 1 && input_string[i + 1] == '0') break; ++input_string_size; ++i; continue; } if (percentCharFound) { switch (input_string[i]) { case 'f': case 'x': case 'X': case 'i': i_d_x_f_found = true; handleNextParameter = true; break; case 'c': case 'e': case 'E': case 'g': case 'o': case 'u': case 'p': case 'n': handleNextParameter = true; break; case 'd': i_d_x_f_found = true; if (paramIter != parameters.end() && *paramIter && (*paramIter)->tokType() != Token::eString) parameterLength = (*paramIter)->str().length(); handleNextParameter = true; break; case 's': if (paramIter != parameters.end() && *paramIter && (*paramIter)->tokType() == Token::eString) parameterLength = Token::getStrLength(*paramIter); handleNextParameter = true; break; } } if (input_string[i] == '%') percentCharFound = !percentCharFound; else if (percentCharFound) { digits_string.append(1, input_string[i]); } if (!percentCharFound) input_string_size++; if (handleNextParameter) { unsigned int tempDigits = static_cast(std::abs(std::atoi(digits_string.c_str()))); if (i_d_x_f_found) tempDigits = std::max(static_cast(tempDigits), 1U); if (digits_string.find('.') != std::string::npos) { const std::string endStr = digits_string.substr(digits_string.find('.') + 1); const unsigned int maxLen = std::max(static_cast(std::abs(std::atoi(endStr.c_str()))), 1U); if (input_string[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) input_string_size += parameterLength; else input_string_size += tempDigits; parameterLength = 0; digits_string.clear(); i_d_x_f_found = false; percentCharFound = false; handleNextParameter = false; if (paramIter != parameters.end()) ++paramIter; } } return input_string_size; } //--------------------------------------------------------------------------- // Checking for allocating insufficient memory for copying a string by // allocating only strlen(src) bytes instead of strlen(src) + 1 bytes (one // extra for the terminating null character). // Example: // char *b = malloc(strlen(a)); // Should be malloc(strlen(a) + 1); // strcpy(b, a); // <== Buffer overrun //--------------------------------------------------------------------------- void CheckBufferOverrun::checkBufferAllocatedWithStrlen() { const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart->next(); tok && tok != scope->classEnd; tok = tok->next()) { unsigned int dstVarId = tok->varId(); if (!dstVarId || tok->strAt(1) != "=") continue; tok = tok->tokAt(2); unsigned int srcVarId; // Look for allocation of a buffer based on the size of a string if (Token::Match(tok, "malloc|g_malloc|g_try_malloc|alloca ( strlen ( %var% ) )")) { const Token *varTok = tok->tokAt(4); srcVarId = varTok->varId(); tok = varTok->tokAt(2); } else if (_tokenizer->isCPP() && Token::Match(tok, "new char [ strlen ( %var% ) ]")) { const Token *varTok = tok->tokAt(5); srcVarId = varTok->varId(); tok = varTok->tokAt(2); } else if (Token::Match(tok, "realloc|g_realloc|g_try_realloc ( %name% , strlen ( %var% ) )")) { const Token *varTok = tok->tokAt(6); srcVarId = varTok->varId(); tok = varTok->tokAt(2); } else continue; // To avoid false positives and added complexity, we will only look for // improper usage of the buffer within the block that it was allocated for (const Token* const end = tok->scope()->classEnd; tok && tok->next() && tok != end; tok = tok->next()) { // If the buffers are modified, we can't be sure of their sizes if (tok->varId() == srcVarId || tok->varId() == dstVarId) break; if (Token::Match(tok, "strcpy ( %varid% , %var% )", dstVarId) && tok->tokAt(4)->varId() == srcVarId) { bufferOverrunError(tok); } } if (!tok) return; } } } //--------------------------------------------------------------------------- // memcpy(temp, "hello world", 50); //--------------------------------------------------------------------------- void CheckBufferOverrun::checkStringArgument() { const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t functionIndex = 0; functionIndex < functions; ++functionIndex) { const Scope * const scope = symbolDatabase->functionScopes[functionIndex]; for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (") || !_settings->library.hasminsize(tok->str())) continue; unsigned int argnr = 1; for (const Token *argtok = tok->tokAt(2); argtok; argtok = argtok->nextArgument(), argnr++) { if (!Token::Match(argtok, "%str% ,|)")) continue; const Token *strtoken = argtok->getValueTokenMinStrSize(); if (!strtoken) continue; const std::vector *minsizes = _settings->library.argminsizes(tok, argnr); if (!minsizes) continue; if (checkMinSizes(*minsizes, tok, Token::getStrSize(strtoken), nullptr, _settings)) bufferOverrunError(argtok); } } } } //--------------------------------------------------------------------------- // Checking for buffer overflow caused by copying command line arguments // into fixed-sized buffers without checking to make sure that the command // line arguments will not overflow the buffer. // // int main(int argc, char* argv[]) // { // char prog[10]; // strcpy(prog, argv[0]); <-- Possible buffer overrun // } //--------------------------------------------------------------------------- void CheckBufferOverrun::checkInsecureCmdLineArgs() { const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Function * function = symbolDatabase->functionScopes[i]->function; if (function) { const Token* tok = function->token; // Get the name of the argv variable unsigned int varid = 0; if (Token::Match(tok, "main ( int %var% , char * %var% [ ] ,|)")) { varid = tok->tokAt(7)->varId(); } else if (Token::Match(tok, "main ( int %var% , char * * %var% ,|)")) { varid = tok->tokAt(8)->varId(); } else continue; // Jump to the opening curly brace tok = symbolDatabase->functionScopes[i]->classStart; // Search within main() for possible buffer overruns involving argv for (const Token* end = tok->link(); tok != end; tok = tok->next()) { // If argv is modified or tested, its size may be being limited properly if (tok->varId() == varid) break; // Match common patterns that can result in a buffer overrun // e.g. strcpy(buffer, argv[0]) if (Token::Match(tok, "strcpy|strcat (")) { const Token *nextArgument = tok->tokAt(2)->nextArgument(); if (nextArgument) tok = nextArgument; else continue; // Ticket #7964 if (Token::Match(tok, "* %varid%", varid) || Token::Match(tok, "%varid% [", varid)) cmdLineArgsError(tok); } } } } } //--------------------------------------------------------------------------- void CheckBufferOverrun::negativeIndexError(const Token *tok, MathLib::bigint index) { std::ostringstream ostr; ostr << "Array index " << index << " is out of bounds."; reportError(tok, Severity::error, "negativeIndex", ostr.str(), CWE786, false); } void CheckBufferOverrun::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, "negativeIndex", errmsg.str(), CWE786, index.isInconclusive()); } CheckBufferOverrun::ArrayInfo::ArrayInfo() : _element_size(0), _declarationId(0) { } CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const SymbolDatabase * symbolDatabase, const unsigned int forcedeclid) : _varname(var->name()), _declarationId((forcedeclid == 0U) ? var->declarationId() : forcedeclid) { for (std::size_t i = 0; i < var->dimensions().size(); i++) _num.push_back(var->dimension(i)); if (var->typeEndToken()->str() == "*") _element_size = symbolDatabase->sizeOfType(var->typeEndToken()); else if (var->typeStartToken()->strAt(-1) == "struct") _element_size = 100; else { _element_size = symbolDatabase->sizeOfType(var->typeEndToken()); } } /** * Create array info with specified data * The intention is that this is only a temporary solution.. all * checking should be based on ArrayInfo from the start and then * this will not be needed as the declare can be used instead. */ CheckBufferOverrun::ArrayInfo::ArrayInfo(unsigned int id, const std::string &name, MathLib::bigint size1, MathLib::bigint n) : _varname(name), _element_size(size1), _declarationId(id) { _num.push_back(n); } CheckBufferOverrun::ArrayInfo CheckBufferOverrun::ArrayInfo::limit(MathLib::bigint value) const { const MathLib::bigint uvalue = std::max(MathLib::bigint(0), value); MathLib::bigint n = 1; for (std::size_t i = 0; i < _num.size(); ++i) n *= _num[i]; if (uvalue > n) n = uvalue; return ArrayInfo(_declarationId, _varname, _element_size, n - uvalue); } MathLib::bigint CheckBufferOverrun::ArrayInfo::numberOfElements() const { if (_num.empty()) return 0; // total number of elements of array.. MathLib::bigint ret = 1; for (std::size_t i = 0; i < _num.size(); ++i) { ret *= _num[i]; } return ret; } MathLib::bigint CheckBufferOverrun::ArrayInfo::totalIndex(const std::vector &indexes) const { MathLib::bigint index = 0; MathLib::bigint elements = 1; for (std::size_t i = 0; i < _num.size(); ++i) { const std::size_t ri = _num.size() - 1U - i; index += indexes[ri].intvalue * elements; elements *= _num[ri]; } return index; } void CheckBufferOverrun::arrayIndexThenCheck() { if (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * const scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { tok = tok->linkAt(1); continue; } if (Token::Match(tok, "%name% [ %var% ]")) { tok = tok->next(); const unsigned 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) { 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", "Array index '" + indexName + "' is used before limits check.\n" "Defensive programming: The variable '" + indexName + "' 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.", CWE398, false); } std::string CheckBufferOverrun::MyFileInfo::toString() const { std::ostringstream ret; for (std::map::const_iterator it = arrayUsage.begin(); it != arrayUsage.end(); ++it) { ret << " first) << '\"' << " index=\"" << it->second.index << '\"' << " fileName=\"" << ErrorLogger::toxml(it->second.fileName) << '\"' << " linenr=\"" << it->second.linenr << "\"/>\n"; } for (std::map::const_iterator it = arraySize.begin(); it != arraySize.end(); ++it) { ret << " first) << '\"' << " size=\"" << it->second << "\"/>\n"; } return ret.str(); } Check::FileInfo* CheckBufferOverrun::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { (void)settings; MyFileInfo *fileInfo = new MyFileInfo; // Array usage.. const SymbolDatabase* const symbolDB = tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDB->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * const scope = symbolDB->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%var% [") && Token::Match(tok->linkAt(1), "] !![") && tok->variable() && tok->variable()->isExtern() && tok->variable()->isGlobal() && tok->next()->astOperand2()) { const ValueFlow::Value *value = tok->next()->astOperand2()->getMaxValue(false); if (value && value->intvalue > 0) { const MathLib::bigint arrayIndex = value->intvalue; std::map::iterator it = fileInfo->arrayUsage.find(tok->str()); if (it != fileInfo->arrayUsage.end() && it->second.index >= arrayIndex) continue; struct MyFileInfo::ArrayUsage arrayUsage; arrayUsage.index = arrayIndex; arrayUsage.fileName = tokenizer->list.file(tok); arrayUsage.linenr = tok->linenr(); fileInfo->arrayUsage[tok->str()] = arrayUsage; } } } } // Arrays.. const std::list &varlist = symbolDB->scopeList.front().varlist; for (std::list::const_iterator it = varlist.begin(); it != varlist.end(); ++it) { const Variable &var = *it; if (!var.isStatic() && var.isArray() && var.dimensions().size() == 1U && var.dimension(0U) > 0U) fileInfo->arraySize[var.name()] = var.dimension(0U); } return fileInfo; } Check::FileInfo * CheckBufferOverrun::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::string ArrayUsage("ArrayUsage"); const std::string ArraySize("ArraySize"); MyFileInfo *fileInfo = new MyFileInfo; for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (e->Name() == ArrayUsage) { const char *array = e->Attribute("array"); const char *arrayIndex = e->Attribute("index"); const char *fileName = e->Attribute("fileName"); const char *linenr = e->Attribute("linenr"); if (!array || !arrayIndex || !MathLib::isInt(arrayIndex) || !fileName || !linenr || !MathLib::isInt(linenr)) continue; struct MyFileInfo::ArrayUsage arrayUsage; arrayUsage.index = MathLib::toLongNumber(arrayIndex); arrayUsage.fileName = fileName; arrayUsage.linenr = MathLib::toLongNumber(linenr); fileInfo->arrayUsage[array] = arrayUsage; } else if (e->Name() == ArraySize) { const char *array = e->Attribute("array"); const char *size = e->Attribute("size"); if (!array || !size || !MathLib::isInt(size)) continue; fileInfo->arraySize[array] = MathLib::toLongNumber(size); } } return fileInfo; } bool CheckBufferOverrun::analyseWholeProgram(const std::list &fileInfo, const Settings&, ErrorLogger &errorLogger) { bool errors = false; // Merge all fileInfo MyFileInfo all; for (std::list::const_iterator it = fileInfo.begin(); it != fileInfo.end(); ++it) { const MyFileInfo *fi = dynamic_cast(*it); if (!fi) continue; // merge array usage for (std::map::const_iterator it2 = fi->arrayUsage.begin(); it2 != fi->arrayUsage.end(); ++it2) { std::map::const_iterator allit = all.arrayUsage.find(it2->first); if (allit == all.arrayUsage.end() || it2->second.index > allit->second.index) all.arrayUsage[it2->first] = it2->second; } // merge array info for (std::map::const_iterator it2 = fi->arraySize.begin(); it2 != fi->arraySize.end(); ++it2) { std::map::const_iterator allit = all.arraySize.find(it2->first); if (allit == all.arraySize.end()) all.arraySize[it2->first] = it2->second; else all.arraySize[it2->first] = -1; } } // Check buffer usage for (std::map::const_iterator it = all.arrayUsage.begin(); it != all.arrayUsage.end(); ++it) { std::map::const_iterator sz = all.arraySize.find(it->first); if (sz != all.arraySize.end() && sz->second > 0 && sz->second < it->second.index) { ErrorLogger::ErrorMessage::FileLocation fileLoc; fileLoc.setfile(it->second.fileName); fileLoc.line = it->second.linenr; std::list locationList; locationList.push_back(fileLoc); std::ostringstream ostr; ostr << "Array " << it->first << '[' << sz->second << "] accessed at index " << it->second.index << " which is out of bounds"; const ErrorLogger::ErrorMessage errmsg(locationList, emptyString, Severity::error, ostr.str(), "arrayIndexOutOfBounds", CWE788, false); errorLogger.reportErr(errmsg); errors = true; } } return errors; } unsigned int CheckBufferOverrun::sizeOfType(const Token *type) const { if (symbolDatabase) return symbolDatabase->sizeOfType(type); return 0; } cppcheck-1.82/lib/checkbufferoverrun.h000066400000000000000000000324471322667425100200550ustar00rootroot00000000000000/* * 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 checkbufferoverrunH #define checkbufferoverrunH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errorlogger.h" #include "mathlib.h" #include "tokenize.h" #include #include #include #include #include class Settings; class SymbolDatabase; class Token; namespace ValueFlow { class Value; } // namespace ValueFlow namespace tinyxml2 { class XMLElement; } // namespace tinyxml2 // CWE ids used static const struct CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer class Variable; /// @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()), symbolDatabase(nullptr) { } /** This constructor is used when running checks. */ CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) , symbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) { } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger); checkBufferOverrun.checkGlobalAndLocalVariable(); if (tokenizer && tokenizer->isMaxTime()) return; checkBufferOverrun.checkStructVariable(); checkBufferOverrun.checkBufferAllocatedWithStrlen(); checkBufferOverrun.checkInsecureCmdLineArgs(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.negativeArraySize(); } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger); checkBufferOverrun.bufferOverrun(); checkBufferOverrun.checkStringArgument(); } /** @brief %Check for buffer overruns (single pass, use ast and valueflow) */ void bufferOverrun(); /** @brief Using array index before bounds check */ void arrayIndexThenCheck(); /** @brief negative size for array */ void negativeArraySize(); /** * @brief Get minimum length of format string result * @param input_string format string * @param parameters given parameters to sprintf * @return minimum length of resulting string */ static MathLib::biguint countSprintfLength(const std::string &input_string, const std::list ¶meters); /** Check for buffer overruns - locate struct variables and check them with the .._CheckScope function */ void checkStructVariable(); /** Check for buffer overruns - locate global variables and local function variables and check them with the checkScope function */ void checkGlobalAndLocalVariable(); /** Check for buffer overruns due to allocating strlen(src) bytes instead of (strlen(src)+1) bytes before copying a string */ void checkBufferAllocatedWithStrlen(); /** Check string argument buffer overruns */ void checkStringArgument(); /** Check for buffer overruns due to copying command-line args to fixed-sized buffers without bounds checking */ void checkInsecureCmdLineArgs(); /** Information about N-dimensional array */ class CPPCHECKLIB ArrayInfo { private: /** number of elements of array */ std::vector _num; /** full name of variable as pattern */ std::string _varname; /** size of each element in array */ MathLib::bigint _element_size; /** declaration id */ unsigned int _declarationId; public: ArrayInfo(); ArrayInfo(const Variable *var, const SymbolDatabase *symbolDatabase, const unsigned int forcedeclid = 0); /** * Create array info with specified data * The intention is that this is only a temporary solution.. all * checking should be based on ArrayInfo from the start and then * this will not be needed as the declare can be used instead. */ ArrayInfo(unsigned int id, const std::string &name, MathLib::bigint size1, MathLib::bigint n); /** Create a copy ArrayInfo where the number of elements have been limited by a value */ ArrayInfo limit(MathLib::bigint value) const; /** array sizes */ const std::vector &num() const { return _num; } /** array size */ MathLib::bigint num(std::size_t index) const { return _num[index]; } void num(std::size_t index, MathLib::bigint number) { _num[index] = number; } /** size of each element */ MathLib::bigint element_size() const { return _element_size; } /** Variable name */ unsigned int declarationId() const { return _declarationId; } void declarationId(unsigned int id) { _declarationId = id; } /** Variable name */ const std::string &varname() const { return _varname; } void varname(const std::string &name) { _varname = name; } MathLib::bigint numberOfElements() const; MathLib::bigint totalIndex(const std::vector &indexes) const; }; /** Check for buffer overruns (based on ArrayInfo) */ void checkScope(const Token *tok, const ArrayInfo &arrayInfo); void checkScope(const Token *tok, std::map arrayInfos); void checkScope_inner(const Token *tok, const ArrayInfo &arrayInfo); /** Check for buffer overruns */ void checkScope(const Token *tok, const std::vector &varname, const ArrayInfo &arrayInfo); /** * Helper function for checkFunctionCall - check a function parameter * \param ftok token for the function name * \param paramIndex on what parameter is the array used * \param arrayInfo the array information * \param callstack call stack. This is used to prevent recursion and to provide better error messages. Pass a empty list from checkScope etc. */ void checkFunctionParameter(const Token &ftok, const unsigned int paramIndex, const ArrayInfo &arrayInfo, const std::list& callstack); /** * Helper function that checks if the array is used and if so calls the checkFunctionCall * @param tok token that matches "%name% (" * @param arrayInfo the array information * \param callstack call stack. This is used to prevent recursion and to provide better error messages. Pass a empty list from checkScope etc. */ void checkFunctionCall(const Token *tok, const ArrayInfo &arrayInfo, std::list callstack); void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector &index); void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector &index); /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: std::string toString() const; struct ArrayUsage { MathLib::bigint index; std::string fileName; unsigned int linenr; }; /* key:arrayName */ std::map arrayUsage; /* key:arrayName, data:arraySize */ std::map arraySize; }; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const; Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger); /** * 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. */ unsigned int sizeOfType(const Token *type) const; private: const SymbolDatabase *symbolDatabase; static bool isArrayOfStruct(const Token* tok, int &position); void arrayIndexOutOfBoundsError(const std::list &callstack, const ArrayInfo &arrayInfo, const std::vector &index); void bufferOverrunError(const Token *tok, const std::string &varnames = emptyString); void bufferOverrunError(const std::list &callstack, const std::string &varnames = emptyString); void strncatUsageError(const Token *tok); void negativeMemoryAllocationSizeError(const Token *tok); // provide a negative value to memory allocation function void negativeArraySizeError(const Token *tok); void outOfBoundsError(const Token *tok, const std::string &what, const bool show_size_info, const MathLib::bigint &supplied_size, const MathLib::bigint &actual_size); void sizeArgumentAsCharError(const Token *tok); void terminateStrncpyError(const Token *tok, const std::string &varname); void bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function); void negativeIndexError(const Token *tok, MathLib::bigint index); void negativeIndexError(const Token *tok, const ValueFlow::Value &index); void cmdLineArgsError(const Token *tok); void pointerOutOfBoundsError(const Token *tok, const Token *index=nullptr, const MathLib::bigint indexvalue=0); void arrayIndexThenCheckError(const Token *tok, const std::string &indexName); void possibleBufferOverrunError(const Token *tok, const std::string &src, const std::string &dst, bool cat); void argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname); void valueFlowCheckArrayIndex(const Token * const tok, const ArrayInfo &arrayInfo); public: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckBufferOverrun c(nullptr, settings, errorLogger); const std::vector indexes(2, 1); c.arrayIndexOutOfBoundsError(nullptr, ArrayInfo(0, "array", 1, 2), indexes); c.bufferOverrunError(nullptr, std::string("buffer")); c.strncatUsageError(nullptr); c.outOfBoundsError(nullptr, "index", true, 2, 1); c.sizeArgumentAsCharError(nullptr); c.terminateStrncpyError(nullptr, "buffer"); c.bufferNotZeroTerminatedError(nullptr, "buffer", "strncpy"); c.negativeIndexError(nullptr, -1); c.cmdLineArgsError(nullptr); c.pointerOutOfBoundsError(nullptr, nullptr, 0); c.arrayIndexThenCheckError(nullptr, "index"); c.possibleBufferOverrunError(nullptr, "source", "destination", false); c.argumentSizeError(nullptr, "function", "array"); c.negativeMemoryAllocationSizeError(nullptr); c.negativeArraySizeError(nullptr); c.reportError(nullptr, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'x[10]' accessed at index 20, which is out of bounds. Otherwise condition 'y==20' is redundant.", CWE119, false); } private: static std::string myName() { return "Bounds checking"; } std::string classInfo() const { return "Out of bounds checking:\n" "- Array index out of bounds detection by value flow analysis\n" "- Dangerous usage of strncat()\n" "- char constant passed as size to function like memset()\n" "- strncpy() leaving string unterminated\n" "- Accessing array with negative index\n" "- Unsafe usage of main(argv, argc) arguments\n" "- Accessing array with index variable before checking its value\n" "- Check for large enough arrays being passed to functions\n" "- Allocating memory with a negative size\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkbufferoverrunH cppcheck-1.82/lib/checkclass.cpp000066400000000000000000003321001322667425100166100ustar00rootroot00000000000000/* * 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 "checkclass.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="; } return ""; } static bool isPureWithoutBody(Function const & func) { return func.isPure() && !func.hasBody(); } //--------------------------------------------------------------------------- CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), symbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) { } //--------------------------------------------------------------------------- // ClassCheck: Check that all class constructors are ok. //--------------------------------------------------------------------------- void CheckClass::constructors() { const bool printStyle = _settings->isEnabled(Settings::STYLE); const bool printWarnings = _settings->isEnabled(Settings::WARNING); if (!printStyle && !printWarnings) return; const bool printInconclusive = _settings->inconclusive; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; bool usedInUnion = false; for (std::list::const_iterator it = symbolDatabase->scopeList.begin(); it != symbolDatabase->scopeList.end(); ++it) { if (it->type != Scope::eUnion) continue; const Scope &unionScope = *it; for (std::list::const_iterator var = unionScope.varlist.begin(); var != unionScope.varlist.end(); ++var) { 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.. std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { if (var->isPrivate() && !var->isStatic() && !Token::Match(var->nameToken(), "%varid% ; %varid% =", var->declarationId()) && (!var->isClass() || (var->type() && var->type()->needInitialization == Type::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 (std::list::const_iterator it = scope->nestedList.begin(); it != scope->nestedList.end(); ++it) { const Scope * const nestedScope = *it; if (nestedScope->type == Scope::eUnion) { bailout = true; break; } } if (bailout) continue; } std::list::const_iterator func; std::vector usage(scope->varlist.size()); for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (!func->hasBody() || !(func->isConstructor() || func->type == Function::eOperatorEqual)) continue; // Mark all variables not used clearAllVar(usage); std::list callstack; initializeVarList(*func, callstack, scope, usage); // Check if any variables are uninitialized std::list::const_iterator var; unsigned int count = 0; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++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->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::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 (!var->isPointer() && !(var->type() && var->type()->needInitialization != Type::True) && (func->type == Function::eCopyConstructor || func->type == Function::eOperatorEqual)) { if (!var->typeStartToken()->isStandardType()) { if (printInconclusive) inconclusive = true; else 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 != Private) { 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->classStart && func->functionScope->classStart->link() == func->functionScope->classStart->next()) { // don't warn about user defined default constructor when there are other constructors if (printInconclusive) uninitVarError(func->token, scope->className, var->name(), true); } else uninitVarError(func->token, scope->className, var->name(), inconclusive); } } } } } } void CheckClass::checkExplicitConstructors() { if (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; // 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 (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 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 && _settings->standards.cpp != Standards::CPP11) continue; for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { // 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 == Private)) continue; if (!func->isExplicit() && func->argCount() == 1 && func->type != Function::eCopyConstructor && func->type != Function::eMoveConstructor) { noExplicitConstructorError(func->tokenDef, scope->className, scope->type == Scope::eStruct); } } } } void CheckClass::copyconstructors() { if (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; std::map allocatedVars; for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eConstructor && func->functionScope) { const Token* tok = func->functionScope->classDef->linkAt(1); for (const Token* const end = func->functionScope->classStart; tok != end; tok = tok->next()) { if (Token::Match(tok, "%var% ( new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { const Variable* var = tok->variable(); if (var && var->isPointer() && var->scope() == scope) allocatedVars[tok->varId()] = tok; } } for (const Token* const end = func->functionScope->classEnd; tok != end; tok = tok->next()) { if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { const Variable* var = tok->variable(); if (var && var->isPointer() && var->scope() == scope && !var->isStatic()) allocatedVars[tok->varId()] = tok; } } } } std::set copiedVars; const Token* copyCtor = nullptr; for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eCopyConstructor) { copyCtor = func->tokenDef; if (func->functionScope) { 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->classStart; tok!=func->functionScope->classEnd; 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); } } } else // non-copyable or implementation not seen allocatedVars.clear(); break; } } if (!copyCtor) { if (!allocatedVars.empty() && scope->definedType->derivedFrom.empty()) // TODO: Check if base class is non-copyable noCopyConstructorError(scope->classDef, scope->className, scope->type == Scope::eStruct); } else { if (!copiedVars.empty()) { for (std::set::const_iterator it = copiedVars.begin(); it != copiedVars.end(); ++it) { copyConstructorShallowCopyError(*it, (*it)->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::style, "copyCtorPointerCopying", "Value of pointer '" + varname + "', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, false); } void CheckClass::noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct) { // The constructor might be intentionally missing. Therefore this is not a "warning" reportError(tok, Severity::style, "noCopyConstructor", std::string(isStruct ? "struct" : "class") + " '" + classname + "' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.", CWE398, false); } bool CheckClass::canNotCopy(const Scope *scope) { std::list::const_iterator func; bool constructor = false; bool publicAssign = false; bool publicCopy = false; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->isConstructor()) constructor = true; if (func->access != 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) { std::list::const_iterator func; bool constructor = false; bool publicAssign = false; bool publicCopy = false; bool publicMove = false; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->isConstructor()) constructor = true; if (func->access != 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(unsigned int varid, const Scope *scope, std::vector &usage) { std::list::const_iterator var; unsigned int count = 0; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++count) { if (var->declarationId() == varid) { usage[count].assign = true; return; } } } void CheckClass::initVar(unsigned int varid, const Scope *scope, std::vector &usage) { std::list::const_iterator var; unsigned int count = 0; for (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 (std::size_t i = 0; i < usage.size(); ++i) usage[i].assign = true; } void CheckClass::clearAllVar(std::vector &usage) { for (std::size_t i = 0; i < usage.size(); ++i) { usage[i].assign = false; usage[i].init = false; } } bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope) { // Iterate through each base class... for (std::size_t i = 0; i < scope->definedType->derivedFrom.size(); ++i) { const Type *derivedFrom = scope->definedType->derivedFrom[i].type; // Check if base class exists in database if (derivedFrom && derivedFrom->classScope) { const std::list& functionList = derivedFrom->classScope->functionList; std::list::const_iterator func; for (func = functionList.begin(); func != functionList.end(); ++func) { 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->classEnd; 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 found if (member) { // 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%")) { 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% (")) { std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { 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% (") && ftok->str() != "if") { 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", "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "' does not have a constructor.\n" "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "' 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") + " '" + classname + "' 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", message + "\n" + verbose, CWE398, false); } void CheckClass::uninitVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive) { reportError(tok, Severity::warning, "uninitMemberVar", "Member variable '" + classname + "::" + varname + "' 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", "Member variable '" + classname + "::" + varname + "' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive); } //--------------------------------------------------------------------------- // ClassCheck: Use initialization list instead of assignment //--------------------------------------------------------------------------- void CheckClass::initializationListUsage() { if (!_settings->isEnabled(Settings::PERFORMANCE)) return; const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // Check every constructor if (!scope->function || (!scope->function->isConstructor())) continue; const Scope* owner = scope->functionOf; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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) != "*") { const Variable* var = tok->variable(); if (var && var->scope() == owner && !var->isStatic()) { if (var->isPointer() || var->isReference() || var->isEnumType() || (!var->type() && !var->isStlStringType() && !(Token::Match(var->typeStartToken(), "std :: %type% <") && !Token::simpleMatch(var->typeStartToken()->linkAt(3), "> ::")))) continue; bool allowed = true; for (const Token* tok2 = tok->tokAt(2); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { const Variable* var2 = tok2->variable(); if (var2) { if (var2->scope() == owner && tok2->strAt(-1)!=".") { // Is there a dependency between two member variables? allowed = false; break; } else if (var2->isArray() && var2->isLocal()) { // Can't initialize with a local array allowed = false; break; } } else if (tok2->str() == "this") { // 'this' instance is not completely constructed in initialization list allowed = false; break; } else if (Token::Match(tok2, "%name% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called? allowed = false; break; } } if (!allowed) continue; suggestInitializationList(tok, tok->str()); } } } } } void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname) { reportError(tok, Severity::performance, "useInitializationList", "Variable '" + varname + "' 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 '" + varname + "' 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->classEnd; 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 != 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 (std::list::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { if (i->isStatic()) { const Token* tok = Token::findmatch(scope->classEnd, "%varid% =|(|{", i->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 (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; // do not check borland classes with properties.. if (Token::findsimplematch(scope->classStart, "; __property ;", scope->classEnd)) continue; std::list privateFuncs; for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { // Get private functions.. if (func->type == Function::eFunction && func->access == 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::list& friendList = scope->definedType->friendList; for (std::list::const_iterator it = friendList.begin(); !used && it != friendList.end(); ++it) { if (it->type) used = checkFunctionUsage(privateFuncs.front(), it->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", "Unused private function: '" + classname + "::" + funcname + "'", 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 = _settings->isEnabled(Settings::WARNING); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; 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) { 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 (")) { 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 = _settings->isEnabled(Settings::PORTABILITY); // recursively check all parent classes for (std::size_t i = 0; i < type->definedType->derivedFrom.size(); i++) { const Type* derivedFrom = type->definedType->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 std::list::const_iterator func; for (func = type->functionList.begin(); func != type->functionList.end(); ++func) { if (func->isVirtual()) { if (allocation) mallocOnClassError(tok, tok->str(), type->classDef, "virtual method"); else memsetError(tok, tok->str(), "virtual method", type->classDef->str()); } } // Warn if type is a class or struct that contains any std::* variables std::list::const_iterator var; for (var = type->varlist.begin(); var != type->varlist.end(); ++var) { 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(); // check for std:: type if (var->isStlType() && tok1->strAt(2) != "array" && !_settings->library.podtype(tok1->strAt(2))) if (allocation) mallocOnClassError(tok, tok->str(), type->classDef, "'std::" + tok1->strAt(2) + "'"); else memsetError(tok, tok->str(), "'std::" + tok1->strAt(2) + "'", 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; toks.push_back(tok); toks.push_back(classTok); reportError(toks, Severity::warning, "mallocOnClassWarning", "Memory for class instance allocated with " + memfunc + "(), but class provides constructors.\n" "Memory for class instance allocated with " + memfunc + "(), 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; toks.push_back(tok); toks.push_back(classTok); reportError(toks, Severity::error, "mallocOnClassError", "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", "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", "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 (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eOperatorEqual && func->access == 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&"" Token *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", "'" + className + "::operator=' should return '" + className + " &'.\n" "The "+className+"::operator= does not conform to standard C/C++ behaviour. To conform to standard C/C++ behaviour, return a reference to self (such as: '"+className+" &"+className+"::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 (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eOperatorEqual && func->hasBody()) { // make sure return signature is correct if (Token::Match(func->retDef, "%type% &") && func->retDef->str() == scope->className) { checkReturnPtrThis(scope, &(*func), func->functionScope->classStart, func->functionScope->classEnd); } } } } } 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; 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() == ";") { std::list::const_iterator it; // check if it is a member function for (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::Match(tok->next(), "(| * this ;|=") || 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 == Public); } else { operatorEqMissingReturnStatementError(func->token, true); } return; } if (_settings->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 == 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 (!_settings->isEnabled(Settings::WARNING)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; // skip classes with multiple inheritance if (scope->definedType->derivedFrom.size() > 1) continue; std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 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->classEnd; for (const Token *tok = func->functionScope->classStart; 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->classEnd; for (const Token *tok = func->functionScope->classStart; tok && tok != last; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; std::stack tokens; tokens.push(tok->next()->astOperand2()); while (!tokens.empty()) { const Token *tok2 = tokens.top(); tokens.pop(); if (!tok2) continue; tokens.push(tok2->astOperand1()); tokens.push(tok2->astOperand2()); if (!Token::Match(tok2, "==|!=")) continue; if (Token::simpleMatch(tok2->astOperand1(), "this")) tok2 = tok2->astOperand2(); else if (Token::simpleMatch(tok2->astOperand2(), "this")) tok2 = tok2->astOperand1(); else continue; if (tok2 && tok2->str() == "&" && !tok2->astOperand2() && tok2->astOperand1() && tok2->astOperand1()->str() == rhs->str()) return true; } } 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 // * base class is deleted // unless inconclusive in which case: // * base class has virtual members but doesn't have virtual destructor const bool printInconclusive = _settings->inconclusive; std::list inconclusiveErrors; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; // Skip base classes (unless inconclusive) if (scope->definedType->derivedFrom.empty()) { if (printInconclusive) { const Function *destructor = scope->getDestructor(); if (destructor && !destructor->isVirtual()) { std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->isVirtual()) { inconclusiveErrors.push_back(destructor); break; } } } } continue; } // 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 (std::size_t j = 0; j < scope->definedType->derivedFrom.size(); ++j) { // Check if base class is public and exists in database if (scope->definedType->derivedFrom[j].access != Private && scope->definedType->derivedFrom[j].type) { const Type *derivedFrom = scope->definedType->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 (std::size_t k = 1; k < symbolDatabase->getVariableListSize(); k++) { const Variable* var = symbolDatabase->getVariableFromVarId(k); 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 = _tokenizer->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->isVirtual()) { // 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 == Public) { virtualDestructorError(baseDestructor->token, derivedFrom->name(), derivedClass->str(), false); // check for duplicate error and remove it if found std::list::iterator found = find(inconclusiveErrors.begin(), inconclusiveErrors.end(), baseDestructor); if (found != inconclusiveErrors.end()) inconclusiveErrors.erase(found); } } } } } } for (std::list::const_iterator i = inconclusiveErrors.begin(); i != inconclusiveErrors.end(); ++i) virtualDestructorError((*i)->tokenDef, (*i)->name(), emptyString, true); } void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) { if (inconclusive) { if (_settings->isEnabled(Settings::WARNING)) reportError(tok, Severity::warning, "virtualDestructor", "Class '" + Base + "' which has virtual members does not have a virtual destructor.", CWE404, true); } else { reportError(tok, Severity::error, "virtualDestructor", "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 (!_settings->isEnabled(Settings::WARNING)) return; const Token *tok = _tokenizer->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 (!_settings->inconclusive) return; if (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { // does the function have a body? if (func->type != Function::eFunction || !func->hasBody()) continue; // don't warn for friend/static/virtual methods if (func->isFriend() || func->isStatic() || func->isVirtual()) 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 { // 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); std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { if (var->name() == tok->str()) { if (tok->varId() == 0) symbolDatabase->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 (std::size_t i = 0; i < scope->definedType->derivedFrom.size(); ++i) { // find the base class const Type *derivedFrom = scope->definedType->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 (std::list::const_iterator i = scope->functionList.cbegin(); i != scope->functionList.cend(); ++i) { if (i->name() == tok->str()) { const Token* tok2 = tok->tokAt(2); size_t argsPassed = tok2->str() == ")" ? 0 : 1; for (;;) { tok2 = tok2->nextArgument(); if (tok2) argsPassed++; else break; } if (argsPassed == i->argCount() || (argsPassed < i->argCount() && argsPassed >= i->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 (std::size_t i = 0; i < scope->definedType->derivedFrom.size(); ++i) { // find the base class const Type *derivedFrom = scope->definedType->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 (std::size_t i = 0; i < scope->definedType->derivedFrom.size(); ++i) { // find the base class const Type *derivedFrom = scope->definedType->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 = make_container< std::set >() << "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->classStart; tok1 && tok1 != func->functionScope->classEnd; 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->tokType() == Token::eAssignmentOp && 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->tokType() == Token::eAssignmentOp) { 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()->tokType() == Token::eAssignmentOp) return false; // Streaming else if (end->strAt(1) == "<<" && tok1->strAt(-1) != "<<") return false; else if (tok1->strAt(-1) == ">>") 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; } // 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", "Technically the member function '" + classname + "::" + funcname + "' can be const.\n" "The member function '" + classname + "::" + funcname + "' 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", "Technically the member function '" + classname + "::" + funcname + "' can be static.\n" "The member function '" + classname + "::" + funcname + "' 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?", 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 (!_settings->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 (!_settings->inconclusive) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; std::list::const_iterator func; // iterate through all member functions looking for constructors for (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->classStart) { if (Token::Match(tok, "%name% (|{")) { const Variable *var = scope->getVariable(tok->str()); if (var) vars.push_back(VarInfo(var, tok)); if (Token::Match(tok->tokAt(2), "%name% =")) { var = scope->getVariable(tok->strAt(2)); if (var) vars.push_back(VarInfo(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 (std::size_t 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; toks.push_back(tok1); toks.push_back(tok2); reportError(toks, Severity::style, "initializerList", "Member variable '" + classname + "::" + varname + "' is in the wrong place in the initializer list.\n" "Member variable '" + classname + "::" + varname + "' 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 (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope* scope = symbolDatabase->functionScopes[i]; const Function* function = scope->function; if (!function || !function->isConstructor()) continue; const Token* tok = function->arg->link()->next(); if (tok->str() != ":") continue; for (; tok != scope->classStart; 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", "Member variable '" + varname + "' is initialized by itself.", CWE665, false); } //--------------------------------------------------------------------------- // Check for pure virtual function calls //--------------------------------------------------------------------------- void CheckClass::checkPureVirtualFunctionCall() { if (! _settings->isEnabled(Settings::WARNING)) return; const std::size_t functions = symbolDatabase->functionScopes.size(); std::map > callsPureVirtualFunctionMap; for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (scope->function == nullptr || !scope->function->hasBody() || !(scope->function->isConstructor() || scope->function->isDestructor())) continue; const std::list & pureVirtualFunctionCalls=callsPureVirtualFunction(*scope->function,callsPureVirtualFunctionMap); for (std::list::const_iterator pureCallIter=pureVirtualFunctionCalls.begin(); pureCallIter!=pureVirtualFunctionCalls.end(); ++pureCallIter) { const Token & pureCall=**pureCallIter; std::list pureFuncStack; pureFuncStack.push_back(&pureCall); getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, pureCall, pureFuncStack); if (!pureFuncStack.empty()) callsPureVirtualFunctionError(*scope->function, pureFuncStack, pureFuncStack.back()->str()); } } } const std::list & CheckClass::callsPureVirtualFunction(const Function & function, std::map > & callsPureVirtualFunctionMap) { std::pair >::iterator, bool > found = callsPureVirtualFunctionMap.insert(std::pair >(&function, std::list())); std::list & pureFunctionCalls = found.first->second; if (found.second) { if (function.hasBody()) { for (const Token *tok = function.arg->link(); tok && tok != function.functionScope->classEnd; 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()->classEnd->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() && (_settings->library.ignorefunction(tok->str()) || _settings->library.ignorefunction(prev->previous()->str()))) continue; } if (isPureWithoutBody(*callFunction)) { pureFunctionCalls.push_back(tok); continue; } const std::list & pureFunctionCallsOfTok = callsPureVirtualFunction(*callFunction, callsPureVirtualFunctionMap); if (!pureFunctionCallsOfTok.empty()) { pureFunctionCalls.push_back(tok); continue; } } } } return pureFunctionCalls; } void CheckClass::getFirstPureVirtualFunctionCallStack( std::map > & callsPureVirtualFunctionMap, const Token & pureCall, std::list & pureFuncStack) { if (isPureWithoutBody(*pureCall.function())) { pureFuncStack.push_back(pureCall.function()->token); return; } std::map >::const_iterator found = callsPureVirtualFunctionMap.find(pureCall.function()); if (found == callsPureVirtualFunctionMap.end() || found->second.empty()) { pureFuncStack.clear(); return; } const Token & firstPureCall = **found->second.begin(); pureFuncStack.push_back(&firstPureCall); getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, firstPureCall, pureFuncStack); } void CheckClass::callsPureVirtualFunctionError( const Function & scopeFunction, const std::list & tokStack, const std::string &purefuncname) { const char * scopeFunctionTypeName = getFunctionTypeName(scopeFunction.type); reportError(tokStack, Severity::warning, "pureVirtualCall", "Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ".\n" "Call of pure virtual function '" + purefuncname + "' 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 (!_settings->isEnabled(Settings::WARNING)) return; // Iterate over all classes for (std::list::const_iterator classIt = symbolDatabase->typeList.begin(); classIt != symbolDatabase->typeList.end(); ++classIt) { // Iterate over the parent classes for (std::vector::const_iterator parentClassIt = classIt->derivedFrom.begin(); parentClassIt != classIt->derivedFrom.end(); ++parentClassIt) { // 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 (std::list::const_iterator classVarIt = classIt->classScope->varlist.begin(); classVarIt != classIt->classScope->varlist.end(); ++classVarIt) { for (std::list::const_iterator parentClassVarIt = parentClassIt->type->classScope->varlist.begin(); parentClassVarIt != parentClassIt->type->classScope->varlist.end(); ++parentClassVarIt) { 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) { std::list toks; toks.push_back(tok1); toks.push_back(tok2); 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(toks, Severity::warning, "duplInheritedMember", message, CWE398, false); } //--------------------------------------------------------------------------- // Check that copy constructor and operator defined together //--------------------------------------------------------------------------- enum CtorType { NO, WITHOUT_BODY, WITH_BODY }; void CheckClass::checkCopyCtorAndEqOperator() { if (!_settings->isEnabled(Settings::WARNING)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; 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; std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 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; if ((copyCtors == CtorType::WITH_BODY && assignmentOperators == CtorType::NO) || (copyCtors == CtorType::NO && assignmentOperators == CtorType::WITH_BODY)) 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 = "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "' has '" + getFunctionTypeName(hasCopyCtor ? Function::eCopyConstructor : Function::eOperatorEqual) + "' but lack of '" + getFunctionTypeName(hasCopyCtor ? Function::eOperatorEqual : Function::eCopyConstructor) + "'."; reportError(tok, Severity::warning, "copyCtorAndEqOperator", message); } void CheckClass::checkUnsafeClassDivZero(bool test) { // style severity: it is a style decision if classes should be safe or // if users should be required to be careful. I expect that many users // will disagree about these reports. if (!_settings->isEnabled(Settings::STYLE)) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * classScope = symbolDatabase->classAndStructScopes[i]; if (!test && classScope->classDef->fileIndex() != 1) continue; std::list::const_iterator func; for (func = classScope->functionList.begin(); func != classScope->functionList.end(); ++func) { if (func->access != AccessControl::Public) continue; if (!func->hasBody()) continue; if (func->name().compare(0,8,"operator")==0) continue; for (const Token *tok = func->functionScope->classStart; tok; tok = tok->next()) { if (Token::Match(tok, "if|switch|while|for|do|}")) break; if (tok->str() != "/") continue; if (!tok->valueType() || !tok->valueType()->isIntegral()) continue; if (!tok->astOperand2()) continue; const Variable *var = tok->astOperand2()->variable(); if (!var || !var->isArgument()) continue; unsafeClassDivZeroError(tok, classScope->className, func->name(), var->name()); break; } } } } void CheckClass::unsafeClassDivZeroError(const Token *tok, const std::string &className, const std::string &methodName, const std::string &varName) { const std::string s = className + "::" + methodName + "()"; reportError(tok, Severity::style, "unsafeClassDivZero", "Public interface of " + className + " is not safe. When calling " + s + ", if parameter " + varName + " is 0 that leads to division by zero."); } cppcheck-1.82/lib/checkclass.h000066400000000000000000000371541322667425100162700ustar00rootroot00000000000000/* * 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 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()), symbolDatabase(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) { if (tokenizer->isC()) return; CheckClass checkClass(tokenizer, settings, errorLogger); // can't be a simplified check .. the 'sizeof' is used. checkClass.checkMemset(); checkClass.checkUnsafeClassDivZero(); } /** @brief Run checks on the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { if (tokenizer->isC()) return; CheckClass checkClass(tokenizer, settings, errorLogger); // Coding style checks 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.checkPureVirtualFunctionCall(); checkClass.checkDuplInheritedMembers(); checkClass.checkExplicitConstructors(); checkClass.checkCopyCtorAndEqOperator(); } /** @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 pure virtual function */ void checkPureVirtualFunctionCall(); /** @brief Check duplicated inherited members */ void checkDuplInheritedMembers(); /** @brief Check that copy constructor and operator defined together */ void checkCopyCtorAndEqOperator(); /** @brief Check that arbitrary usage of the public interface does not result in division by zero */ void checkUnsafeClassDivZero(bool test=false); private: const SymbolDatabase *symbolDatabase; // 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 Token *tok, const std::string &classname, bool isStruct); void uninitVarError(const Token *tok, 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 callsPureVirtualFunctionError(const Function & scopeFunction, const std::list & tokStack, const std::string &purefuncname); 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 unsafeClassDivZeroError(const Token *tok, const std::string &className, const std::string &methodName, const std::string &varName); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { 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, "class", false); c.uninitVarError(nullptr, "classname", "varname", 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.unsafeClassDivZeroError(nullptr, "Class", "dostuff", "x"); } static std::string myName() { return "Class"; } std::string classInfo() const { 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" "- 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"; } // 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(unsigned 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(unsigned 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 pure virtual functions are called directly or indirectly * @param function function to be checked * @param callsPureVirtualFunctionMap map of results for already checked functions * @return list of tokens where pure virtual functions are called */ const std::list & callsPureVirtualFunction( const Function & function, std::map > & callsPureVirtualFunctionMap); /** * @brief looks for the first pure virtual function call stack * @param callsPureVirtualFunctionMap map of results obtained from callsPureVirtualFunction * @param pureCall token where pure virtual function is called directly or indirectly * @param[in,out] pureFuncStack list to append the stack */ void getFirstPureVirtualFunctionCallStack( std::map > & callsPureVirtualFunctionMap, const Token & pureCall, std::list & pureFuncStack); static bool canNotCopy(const Scope *scope); static bool canNotMove(const Scope *scope); }; /// @} //--------------------------------------------------------------------------- #endif // checkclassH cppcheck-1.82/lib/checkcondition.cpp000066400000000000000000001662061322667425100175050ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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::isAliased(const std::set &vars) const { for (const Token *tok = _tokenizer->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 (!_settings->isEnabled(Settings::STYLE)) return; for (const Token *tok = _tokenizer->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, "[(,] &"); unsigned 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 unsigned 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 (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, _settings)) 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; } } 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) { std::list locations; locations.push_back(tok1); locations.push_back(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; locations.push_back(tok1); locations.push_back(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 (!_settings->isEnabled(Settings::WARNING)) return; for (const Token *tok = _tokenizer->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()->values().size() == 1 && tok->astOperand1()->values().front().intvalue != 0 && tok->astOperand1()->values().front().isKnown()) || (tok->astOperand2()->values().size() == 1 && tok->astOperand2()->values().front().intvalue != 0 && tok->astOperand2()->values().front().isKnown()); 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 (!_settings->isEnabled(Settings::STYLE)) return; for (const Token *tok = _tokenizer->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 (std::list::const_iterator num = numbers.begin(); num != numbers.end(); ++num) { const MathLib::bigint num1 = *num; 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(_tokenizer->isCPP(), true, cond1, cond2, _settings->library, pure)) 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(_tokenizer->isCPP(), true, expr1, expr2, _settings->library, pure)) 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::multiCondition() { if (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eIf) continue; const Token * const cond1 = i->classDef->next()->astOperand2(); const Token * tok2 = i->classDef->next(); 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 (isOverlappingCond(cond1, tok2->astOperand2(), true)) multiConditionError(tok2, cond1->linenr()); } } } void CheckCondition::multiConditionError(const Token *tok, unsigned int line1) { 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); } //--------------------------------------------------------------------------- // - 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 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 std::stack tokens; tokens.push(condTok); while (!tokens.empty()) { const Token *cond = tokens.top(); tokens.pop(); if (!cond) continue; if (Token::Match(cond, "%name% (")) { nonConstFunctionCall = isNonConstFunctionCall(cond, _settings->library); if (nonConstFunctionCall) break; } 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% .") || (_tokenizer->isCPP() && cond->str() == "this"); } else { tokens.push(cond->astOperand1()); tokens.push(cond->astOperand2()); } } if (nonConstFunctionCall) continue; // parse until second condition is reached.. enum MULTICONDITIONTYPE { INNER, AFTER } type; const Token *tok; if (Token::Match(scope->classStart, "{ return|throw|continue|break")) { tok = scope->classEnd->next(); type = MULTICONDITIONTYPE::AFTER; } else { tok = scope->classStart; type = MULTICONDITIONTYPE::INNER; } const Token * const endToken = tok->scope()->classEnd; for (; tok && tok != endToken; tok = tok->next()) { if (Token::simpleMatch(tok, "if (")) { // Does condition modify tracked variables? if (const Token *op = Token::findmatch(tok, "++|--", tok->linkAt(1))) { 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(), "++|--", tok->linkAt(1)); } if (bailout) break; } // Condition.. const Token *cond2 = tok->next()->astOperand2(); 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 (isOppositeCond(false, _tokenizer->isCPP(), firstCondition, cond2, _settings->library, true)) { if (!isAliased(vars)) oppositeInnerConditionError(firstCondition, cond2); } } } 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 (isSameExpression(_tokenizer->isCPP(), true, cond1, secondCondition, _settings->library, true)) { if (!isAliased(vars)) identicalConditionAfterEarlyExitError(cond1, secondCondition); } } } } if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, _settings->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 (std::set::const_iterator it = vars.begin(); it != vars.end(); ++it) { if (isVariableChanged(tok1, tok2, *it, nonlocal, _settings)) { 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(), ".|[") || (Token::simpleMatch(parent->astParent(), "*") && !parent->astParent()->astOperand2())) parent = parent->astParent(); if (Token::Match(parent->astParent(), "%assign%")) break; } if (_tokenizer->isCPP() && Token::Match(tok, "%name% <<|>>") && (!tok->valueType() || !tok->valueType()->isIntegral())) break; if (_tokenizer->isCPP() && Token::simpleMatch(tok->previous(), ">>")) { const Token *rhs = tok->previous()->astOperand1(); if (!rhs || !rhs->valueType() || !rhs->valueType()->isIntegral()) 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; } } } } void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2) { const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "!x"); ErrorPath errorPath; errorPath.push_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.push_back(ErrorPathItem(tok2, "opposite inner condition: " + s2)); const std::string msg("Opposite inner 'if' condition leads to a dead code block.\n" "Opposite inner 'if' 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::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2) { const std::string cond(cond1 ? cond1->expressionString() : "x"); ErrorPath errorPath; errorPath.push_back(ErrorPathItem(cond1, "first condition")); errorPath.push_back(ErrorPathItem(cond2, "second condition")); reportError(errorPath, Severity::warning, "identicalConditionAfterEarlyExit", "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")); } void CheckCondition::checkIncorrectLogicOperator() { const bool printStyle = _settings->isEnabled(Settings::STYLE); const bool printWarning = _settings->isEnabled(Settings::WARNING); if (!printWarning && !printStyle) return; const bool printInconclusive = _settings->inconclusive; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t ii = 0; ii < functions; ++ii) { const Scope * scope = symbolDatabase->functionScopes[ii]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2()) continue; // Opposite comparisons around || or && => always true or always false if ((tok->astOperand1()->isName() || tok->astOperand2()->isName()) && isOppositeCond(true, _tokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), _settings->library, true)) { const bool alwaysTrue(tok->str() == "||"); incorrectLogicOperatorError(tok, tok->expressionString(), alwaysTrue, false); 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, _tokenizer->isCPP(), tok->astOperand1(), tok2, _settings->library, true)) { std::string expr1(tok->astOperand1()->expressionString()); std::string expr2(tok->astOperand2()->astOperand1()->expressionString()); std::string expr3(tok->astOperand2()->astOperand2()->expressionString()); // make copy for later because the original string might get overwritten const std::string expr1VerboseMsg = expr1; const std::string expr2VerboseMsg = expr2; const std::string expr3VerboseMsg = expr3; if (expr1.length() + expr2.length() + expr3.length() > 50U) { 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; // Parse LHS bool not1; std::string op1, value1; const Token *expr1; if (!parseComparison(comp1, ¬1, &op1, &value1, &expr1, &inconclusive)) continue; // Parse RHS bool not2; std::string op2, value2; const Token *expr2; if (!parseComparison(comp2, ¬2, &op2, &value2, &expr2, &inconclusive)) continue; if (inconclusive && !printInconclusive) continue; if (isSameExpression(_tokenizer->isCPP(), true, comp1, comp2, _settings->library, true)) continue; // same expressions => only report that there are same expressions if (!isSameExpression(_tokenizer->isCPP(), true, expr1, expr2, _settings->library, true)) continue; const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2); // 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); } 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) { if (always) reportError(tok, 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(tok, 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 (!_settings->isEnabled(Settings::STYLE)) return; const bool isC = _tokenizer->isC(); const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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->tokType() == Token::eComparisonOp) { // 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->str() != "&" || tok->astOperand2())) { 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->astOperand1(), "%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 (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->link()) // don't write false positives when templates are used continue; if (!tok->hasKnownIntValue()) continue; if (Token::Match(tok, "[01]")) continue; const bool constIfWhileExpression = tok->astParent() && Token::Match(tok->astParent()->astOperand1(), "if|while") && !tok->isBoolean(); const bool constValExpr = Token::Match(tok, "%num%|%char%") && tok->astParent() && Token::Match(tok->astParent(),"&&|%oror%|?"); // just one number or char in boolean expression const bool compExpr = Token::Match(tok, "%comp%|!"); // a compare expression if (!(constIfWhileExpression || constValExpr || compExpr)) continue; // Don't warn in assertions. Condition is often 'always true' by intention. // If platform,defines,etc cause 'always false' then that is not dangerous neither. bool assertFound = false; for (const Token * tok2 = tok->astParent(); tok2 ; tok2 = tok2->astParent()) { // move backwards and try to find "assert" if (tok2->str() == "(" && tok2->astOperand2()) { const std::string& str = tok2->previous()->str(); if ((str.find("assert")!=std::string::npos || str.find("ASSERT")!=std::string::npos)) assertFound = true; break; } } if (assertFound) 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 condvalue = value && (value->intvalue != 0); const std::string expr = tok ? tok->expressionString() : std::string("x"); const std::string errmsg = "Condition '" + expr + "' is always " + (condvalue ? "true" : "false"); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::style, "knownConditionTrueFalse", errmsg, (condvalue ? CWE571 : CWE570), false); } void CheckCondition::checkInvalidTestForOverflow() { if (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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(_tokenizer->isCPP(), true, exprToken, calcToken->astOperand1(), _settings->library, true)) termToken = calcToken->astOperand2(); else if (isSameExpression(_tokenizer->isCPP(), true, exprToken, calcToken->astOperand2(), _settings->library, true)) 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) { std::string errmsg; 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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."); } cppcheck-1.82/lib/checkcondition.h000066400000000000000000000174001322667425100171410ustar00rootroot00000000000000/* * 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 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) { CheckCondition checkCondition(tokenizer, settings, errorLogger); checkCondition.multiCondition(); checkCondition.clarifyCondition(); // not simplified because ifAssign checkCondition.multiCondition2(); checkCondition.checkIncorrectLogicOperator(); checkCondition.checkInvalidTestForOverflow(); checkCondition.alwaysTrueFalse(); checkCondition.checkPointerAdditionResultNotNull(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckCondition checkCondition(tokenizer, settings, errorLogger); checkCondition.assignIf(); 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 unsigned 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(); /** 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(); private: 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 multiConditionError(const Token *tok, unsigned int line1); void oppositeInnerConditionError(const Token *tok1, const Token* tok2); void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2); void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive); 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckCondition c(nullptr, settings, errorLogger); c.assignIfError(nullptr, nullptr, emptyString, false); c.badBitmaskCheckError(nullptr); c.comparisonError(nullptr, "&", 6, "==", 1, false); c.multiConditionError(nullptr,1); c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1); c.oppositeInnerConditionError(nullptr, nullptr); c.identicalConditionAfterEarlyExitError(nullptr, nullptr); c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false); 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); } static std::string myName() { return "Condition"; } std::string classInfo() const { 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" "- 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.82/lib/checkexceptionsafety.cpp000066400000000000000000000276141322667425100207300ustar00rootroot00000000000000/* * 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 "checkexceptionsafety.h" #include "settings.h" #include "symboldatabase.h" #include #include #include //--------------------------------------------------------------------------- // Register CheckExceptionSafety.. namespace { CheckExceptionSafety instance; } //--------------------------------------------------------------------------- void CheckExceptionSafety::destructors() { if (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); // Perform check.. const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; const Function * function = scope->function; if (function) { // only looking for destructors if (function->type == Function::eDestructor) { // Inspect this destructor. for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; 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 (!_settings->isEnabled(Settings::WARNING)) return; const bool printInconclusive = _settings->inconclusive; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); // Deallocate a global/member pointer and then throw exception // the pointer will be a dead pointer const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; 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->classEnd) 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()->classEnd; 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 (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eCatch) continue; const unsigned int varid = i->classStart->tokAt(-2)->varId(); if (varid) { for (const Token* tok = i->classStart->next(); tok && tok != i->classEnd; 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 (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->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 = i->classStart->tokAt(-2)->variable(); if (var && var->isClass() && !var->isPointer() && !var->isReference()) catchExceptionByValueError(i->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->classStart->next(); tok != function->functionScope->classEnd; 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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; 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 (!_settings->isEnabled(Settings::STYLE) || !_settings->inconclusive) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // 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->classStart->next(); tok != scope->function->functionScope->classEnd; 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.82/lib/checkexceptionsafety.h000066400000000000000000000164511322667425100203720ustar00rootroot00000000000000/* * 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 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) { } /** Checks that uses the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { 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) { std::string str1(tok1 ? tok1->str() : "foo"); const std::list locationList = make_container< std::list > () << 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 { 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 { 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.82/lib/checkfunctions.cpp000066400000000000000000000546201322667425100175230ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // Check functions //--------------------------------------------------------------------------- #include "checkfunctions.h" #include "astutils.h" #include "mathlib.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckFunctions instance; } static const CWE CWE252(252U); // Unchecked Return Value static const CWE CWE477(477U); // Use of Obsolete Functions static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const CWE CWE686(686U); // Function Call With Incorrect Argument Type static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument void CheckFunctions::checkProhibitedFunctions() { const bool checkAlloca = _settings->isEnabled(Settings::WARNING) && ((_settings->standards.c >= Standards::C99 && _tokenizer->isC()) || _settings->standards.cpp >= Standards::CPP11); const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int i = 0; i < symbolDatabase->functionScopes.size(); i++) { const Scope* scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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 (_tokenizer->isC()) { if (_settings->standards.c > Standards::C89) reportError(tok, Severity::warning, "allocaCalled", "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", "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 = _settings->library.getWarnInfo(tok); if (wi) { if (_settings->isEnabled(wi->severity) && _settings->standards.c >= wi->standards.c && _settings->standards.cpp >= wi->standards.cpp) { reportError(tok, wi->severity, tok->str() + "Called", wi->message, CWE477, false); } } } } } } //--------------------------------------------------------------------------- // Check and //--------------------------------------------------------------------------- void CheckFunctions::invalidFunctionUsage() { const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% ( !!)")) continue; const Token * const functionToken = tok; const std::vector arguments = getArguments(tok); for (unsigned int argnr = 1; argnr <= arguments.size(); ++argnr) { const Token * const argtok = arguments[argnr-1]; // check ... const ValueFlow::Value *invalidValue = argtok->getInvalidValue(functionToken,argnr,_settings); if (invalidValue) { invalidFunctionArgError(argtok, functionToken->next()->astOperand1()->expressionString(), argnr, invalidValue, _settings->library.validarg(functionToken, argnr)); } if (astIsBool(argtok)) { // check if (_settings->library.isboolargbad(functionToken, argnr)) invalidFunctionArgBoolError(argtok, functionToken->str(), argnr); // Are the values 0 and 1 valid? else if (!_settings->library.isargvalid(functionToken, argnr, 0)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, _settings->library.validarg(functionToken, argnr)); else if (!_settings->library.isargvalid(functionToken, argnr, 1)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, _settings->library.validarg(functionToken, argnr)); } } } } } void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) { std::ostringstream errmsg; if (invalidValue && invalidValue->condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition) << " or " << functionName << "() argument nr " << argnr << " can have invalid value."; else errmsg << "Invalid " << functionName << "() argument nr " << argnr << '.'; if (invalidValue) errmsg << " The value is " << invalidValue->intvalue << " 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 << "Invalid " << functionName << "() argument nr " << argnr << ". A non-boolean value is required."; reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, false); } //--------------------------------------------------------------------------- // Check for ignored return values. //--------------------------------------------------------------------------- void CheckFunctions::checkIgnoredReturnValue() { if (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { // skip c++11 initialization, ({...}) if (Token::Match(tok, "%var%|(|, {")) 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()->classEnd; continue; } if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && _settings->library.isUseRetVal(tok) && !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", "Return value of function " + function + "() is not used.", CWE252, false); } //--------------------------------------------------------------------------- // Detect passing wrong values to functions like atan(0, x); //--------------------------------------------------------------------------- void CheckFunctions::checkMathFunctions() { const bool styleC99 = _settings->isEnabled(Settings::STYLE) && _settings->standards.c != Standards::C89 && _settings->standards.cpp != Standards::CPP03; const bool printWarnings = _settings->isEnabled(Settings::WARNING); const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 ( %num% )")) { const std::string& number = tok->strAt(2); bool isNegative = MathLib::isNegative(number); bool isInt = MathLib::isInt(number); bool isFloat = MathLib::isFloat(number); if (isNegative && isInt && MathLib::toLongNumber(number) <= 0) { mathfunctionCallWarning(tok); // case log(-2) } else if (isNegative && isFloat && MathLib::toDoubleNumber(number) <= 0.) { mathfunctionCallWarning(tok); // case log(-2.0) } else if (!isNegative && isFloat && MathLib::toDoubleNumber(number) <= 0.) { mathfunctionCallWarning(tok); // case log(0.0) } else if (!isNegative && isInt && MathLib::toLongNumber(number) <= 0) { mathfunctionCallWarning(tok); // case log(0) } } // acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond else if (Token::Match(tok, "acos|acosl|acosf|asin|asinf|asinl ( %num% )")) { if (std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0) mathfunctionCallWarning(tok); } // sqrt( x ): if x is negative the result is undefined else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )")) { if (MathLib::isNegative(tok->strAt(2))) 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 unsigned int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::warning, "wrongmathcall", "Passing value " + tok->strAt(2) + " to " + tok->str() + "() leads to implementation-defined result.", CWE758, false); else if (numParam == 2) reportError(tok, Severity::warning, "wrongmathcall", "Passing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = _settings->isEnabled(Settings::PORTABILITY); const bool printWarning = _settings->isEnabled(Settings::WARNING); if (!printWarning && !printPortability) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok && (tok != scope->classEnd); 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 = _settings->signedCharMin(); const long long uCharMax = _settings->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 (!_settings->checkLibrary || !_settings->isEnabled(Settings::INFORMATION)) return; bool New = false; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (!tok->scope() || !tok->scope()->isExecutable()) continue; if (tok->str() == "new") New = true; else if (tok->str() == ";") New = false; else if (New) 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 (!_settings->library.isNotLibraryFunction(tok)) continue; const std::string &functionName = _settings->library.getFunctionName(tok); if (functionName.empty() || _settings->library.functions.find(functionName) != _settings->library.functions.end()) continue; reportError(tok, Severity::information, "checkLibraryFunction", "--check-library: There is no matching configuration for function " + functionName + "()"); } } cppcheck-1.82/lib/checkfunctions.h000066400000000000000000000133551322667425100171700ustar00rootroot00000000000000/* * 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 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) { CheckFunctions checkFunctions(tokenizer, settings, errorLogger); // Checks checkFunctions.checkIgnoredReturnValue(); // --check-library : functions with nonmatching configuration checkFunctions.checkLibraryMatchFunctions(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckFunctions checkFunctions(tokenizer, settings, errorLogger); 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 ignoredReturnValueError(const Token* tok, const std::string& function); void mathfunctionCallWarning(const Token *tok, const unsigned 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 { 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.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 { 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.82/lib/checkinternal.cpp000066400000000000000000000404031322667425100173210ustar00rootroot00000000000000/* * 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 . */ #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 = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = _tokenizer->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 (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(); // 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 = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = make_container< std::set > () << "%any%" << "%assign%" << "%bool%" << "%char%" << "%comp%" << "%num%" << "%op%" << "%cop%" << "%or%" << "%oror%" << "%str%" << "%type%" << "%name%" << "%var%" << "%varid%"; } void CheckInternal::checkMissingPercentCharacter() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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::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.82/lib/checkinternal.h000066400000000000000000000117651322667425100167770ustar00rootroot00000000000000/* * 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 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) { } /** Simplified checks. The token list is simplified. */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { 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(); } /** @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(); 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 { 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 { // 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.82/lib/checkio.cpp000066400000000000000000003275671322667425100161370ustar00rootroot00000000000000/* * 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 "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 (_tokenizer->isC()) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; 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; unsigned int mode_indent; enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation; unsigned 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 = make_container< std::set > () << "clearerr" << "feof" << "ferror" << "fgetpos" << "ftell" << "setbuf" << "setvbuf" << "ungetc" << "ungetwc"; } void CheckIO::checkFileUsage() { const bool windows = _settings->isWindowsPlatform(); const bool printPortability = _settings->isEnabled(Settings::PORTABILITY); const bool printWarnings = _settings->isEnabled(Settings::WARNING); std::map filepointers; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); std::size_t varListSize = symbolDatabase->getVariableListSize(); for (std::size_t i = 1; i < varListSize; ++i) { const Variable* var = symbolDatabase->getVariableFromVarId(i); 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 } } std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t j = 0; j < functions; ++j) { const Scope * scope = symbolDatabase->functionScopes[j]; unsigned int indent = 0; for (const Token *tok = scope->classStart; tok != scope->classEnd; 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" || _settings->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 { 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") && !_settings->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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t j = 0; j < functions; ++j) { const Scope * scope = symbolDatabase->functionScopes[j]; for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; 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) { 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(unsigned int arg, const Token *firstArg, const Token **formatStringTok, const Token **formatArgTok) { const Token* argTok = firstArg; for (unsigned 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()) { const ValueFlow::Value &value = argTok->values().front(); if (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 = _tokenizer->getSymbolDatabase(); const bool isWindows = _settings->isWindowsPlatform(); std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t j = 0; j < functions; ++j) { const Scope * scope = symbolDatabase->functionScopes[j]; for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; 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) == "(" && _settings->library.formatstr_function(tok)) { formatStringArgNo = _settings->library.formatstr_argno(tok); scan = _settings->library.formatstr_scan(tok); scanf_s = _settings->library.formatstr_secure(tok); } if (formatStringArgNo >= 0) { // formatstring found in library. Find format string and first argument belonging to format string. if (!findFormat(static_cast(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 printWarning = _settings->isEnabled(Settings::WARNING); const std::string &formatString = formatStringTok->str(); // Count format string parameters.. unsigned int numFormat = 0; unsigned 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; unsigned 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 = static_cast(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, _settings, _tokenizer->isCPP()); if (argInfo.typeToken && !argInfo.isLibraryType(_settings)) { 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()) { 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()) { 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")) 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 != 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.isArrayOrPointer() && 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")) 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) == *i) { if (i+1 != formatString.end()) { if (!isalpha(*(i+2))) { std::string modifier; modifier += *i; modifier += *(i+1); invalidLengthModifierError(tok, numFormat, modifier); done = true; } else { specifier = *i++; specifier += *i++; } } else { done = true; } } else { if (i != formatString.end()) { if (!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 != 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.. unsigned int numFunction = 0; while (argListTok2) { numFunction++; argListTok2 = argListTok2->nextArgument(); // Find next argument } if (printWarning) { // Check that all parameter positions reference an actual parameter for (std::set::const_iterator it = parameterPositionsUsed.begin() ; it != parameterPositionsUsed.end() ; ++it) { if ((*it == 0) || (*it > numFormat)) wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), *it, 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(nullptr); 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::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 (unsigned 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(nullptr); 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(nullptr); 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(nullptr); 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(nullptr); 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 = make_container< std::set >() << "array" << "vector"; const std::set stl_string = make_container< std::set >() << "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(nullptr); 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 (std::size_t i = 0, size = derivedFrom.size(); i < size; ++i) { const Token* nameTok = derivedFrom[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(nullptr); 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) { std::list::const_iterator functions; for (functions = classScope->functionList.begin(); functions != classScope->functionList.end(); ++functions) { if (functions->name() == "operator[]") { if (Token::Match(functions->retDef, "%type% &")) { typeToken = functions->retDef; return true; } } } } } return false; } namespace { const std::set stl_container = make_container< std::set >() << "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()) { const std::vector& derivedFrom = variable->type()->derivedFrom; for (std::size_t i = 0, size = derivedFrom.size(); i < size; ++i) { const Token* nameTok = derivedFrom[i].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, unsigned int numFormat, unsigned int numFunction) { Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning; if (severity != Severity::error && !_settings->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, unsigned int index, unsigned int numFunction) { if (!_settings->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, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!_settings->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, unsigned int numFormat, const std::string& modifier) { if (!_settings->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, unsigned 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 (!_settings->inconclusive || !_settings->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.82/lib/checkio.h000066400000000000000000000204541322667425100155650ustar00rootroot00000000000000/* * 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 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) { CheckIO checkIO(tokenizer, settings, errorLogger); checkIO.checkWrongPrintfScanfArguments(); } /** @brief Run checks on the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckIO checkIO(tokenizer, settings, errorLogger); 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, unsigned int numFormat, unsigned int numFunction); void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, unsigned int index, unsigned int numFunction); void invalidScanfArgTypeError_s(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidScanfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned); void invalidScanfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidLengthModifierError(const Token* tok, unsigned int numFormat, const std::string& modifier); void invalidScanfFormatWidthError(const Token* tok, unsigned 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 { 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.wrongPrintfScanfPosixParameterPositionError(nullptr, "printf", 2, 1); } static std::string myName() { return "IO using format string"; } std::string classInfo() const { 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.82/lib/checkleakautovar.cpp000066400000000000000000000707531322667425100200360ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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); //--------------------------------------------------------------------------- void VarInfo::print() { std::cout << "size=" << alloctype.size() << std::endl; std::map::const_iterator it; for (it = alloctype.begin(); it != alloctype.end(); ++it) { std::string strusage; std::map::const_iterator use = possibleUsage.find(it->first); if (use != possibleUsage.end()) strusage = use->second; std::string status; switch (it->second.status) { 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(); std::map::const_iterator it; for (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(_tokenizer, _errorLogger, _settings); if (_settings->library.isresource(type)) checkmemleak.resourceLeakError(tok, varname); else checkmemleak.memleakError(tok, varname); } void CheckLeakAutoVar::mismatchError(const Token *tok, const std::string &varname) { const CheckMemoryLeak c(_tokenizer, _errorLogger, _settings); std::list callstack(1, tok); c.mismatchAllocDealloc(callstack, varname); } void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) { const CheckMemoryLeak c(_tokenizer, _errorLogger, _settings); c.deallocuseError(tok, varname); } void CheckLeakAutoVar::deallocReturnError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "deallocret", "Returning/dereferencing '" + varname + "' after it is deallocated / released", CWE672, false); } void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName) { if (_settings->checkLibrary && _settings->isEnabled(Settings::INFORMATION)) { reportError(tok, Severity::information, "checkLibraryUseIgnore", "--check-library: Function " + functionName + "() should have / configuration"); } } void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type) { if (_settings->library.isresource(type)) reportError(tok, Severity::error, "doubleFree", "Resource handle '" + varname + "' freed twice.", CWE415, false); else reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname + "' is freed twice.", CWE415, false); } void CheckLeakAutoVar::check() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Local variables that are known to be non-zero. const std::set notzero; // Check function scopes const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (scope->hasInlineOrLambdaFunction()) continue; // Empty variable info VarInfo varInfo; checkScope(scope->classStart, &varInfo, notzero); 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->classEnd, varInfo); } } static bool isVarUsedInTree(const Token *tok, unsigned int varid) { if (!tok) return false; if (tok->varId() == varid) return true; return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid); } void CheckLeakAutoVar::checkScope(const Token * const startToken, VarInfo *varInfo, std::set notzero) { 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()->classEnd; if (!tok) // Ticket #6666 (crash upon invalid code) break; } // Deallocation and then dereferencing pointer.. if (tok->varId() > 0) { const std::map::const_iterator var = alloctype.find(tok->varId()); if (var != alloctype.end()) { bool unknown = false; if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok,unknown) && !unknown) { deallocUseError(tok, tok->str()); } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) { varInfo->erase(tok->varId()); } else if (tok->strAt(-1) == "=") { varInfo->erase(tok->varId()); } } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) { varInfo->referenced.insert(tok->tokAt(2)->varId()); } } if (tok->str() == "(" && tok->previous()->isName()) { VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); functionCall(tok->previous(), varInfo, allocation, nullptr); tok = tok->link(); 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% =")) { // taking address of another variable.. if (Token::Match(varTok->next(), "= %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()); } } } } // is variable used in rhs? if (isVarUsedInTree(varTok->next()->astOperand2(), varTok->varId())) continue; // Variable has already been allocated => error if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end()) leakIfAllocated(varTok, *varInfo); varInfo->erase(varTok->varId()); // not a local variable nor argument? const Variable *var = varTok->variable(); if (var && !var->isArgument() && (!var->isLocal() || var->isStatic())) continue; // Don't check reference variables if (var && var->isReference()) continue; // non-pod variable if (_tokenizer->isCPP()) { if (!var) continue; // Possibly automatically deallocated memory if (!var->typeStartToken()->isStandardType() && Token::Match(varTok, "%var% = new")) continue; if (!var->isPointer() && !var->typeStartToken()->isStandardType()) continue; } // allocation? if (varTok->next()->astOperand2() && Token::Match(varTok->next()->astOperand2()->previous(), "%type% (")) { const Library::AllocFunc* f = _settings->library.alloc(varTok->next()->astOperand2()->previous()); if (f && f->arg == -1) { VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; varAlloc.type = f->groupId; varAlloc.status = VarInfo::ALLOC; } } else if (_tokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) { const Token* tok2 = varTok->tokAt(2)->astOperand1(); bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; varAlloc.type = arrayNew ? -2 : -1; varAlloc.status = VarInfo::ALLOC; } // 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 for (const Token *innerTok = tok->tokAt(2); innerTok; innerTok = innerTok->next()) { if (Token::Match(innerTok, "%var% =")) { // allocation? if (Token::Match(innerTok->tokAt(2), "%type% (")) { const Library::AllocFunc* f = _settings->library.alloc(innerTok->tokAt(2)); if (f && f->arg == -1) { VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; varAlloc.type = f->groupId; varAlloc.status = VarInfo::ALLOC; } } else if (_tokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) { const Token* tok2 = innerTok->tokAt(2)->astOperand1(); bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; varAlloc.type = arrayNew ? -2 : -1; varAlloc.status = VarInfo::ALLOC; } } if (innerTok->str() == ")") break; if (innerTok->str() == "(" && innerTok->previous()->isName()) { VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); functionCall(innerTok->previous(), varInfo, allocation, nullptr); innerTok = innerTok->link(); } } const Token *tok2 = tok->linkAt(1); if (Token::simpleMatch(tok2, ") {")) { 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() == "&&") { 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 (unsigned int i = 0; i < params.size(); ++i) { const Token *par = params[i]; 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(tok2->next(), &varInfo1, notzero); tok2 = tok2->linkAt(1); if (Token::simpleMatch(tok2, "} else {")) { checkScope(tok2->tokAt(2), &varInfo2, notzero); tok = tok2->linkAt(2)->previous(); } else { tok = tok2->previous(); } VarInfo old; old.swap(*varInfo); std::map::const_iterator it; for (it = old.alloctype.begin(); it != old.alloctype.end(); ++it) { const unsigned 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.status == VarInfo::DEALLOC && 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.status == VarInfo::DEALLOC && 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 (_tokenizer->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(); } // Function call.. else if (Token::Match(ftok, "%type% (")) { const Library::AllocFunc* af = _settings->library.dealloc(ftok); VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC); if (allocation.type == 0) allocation.status = VarInfo::NOALLOC; functionCall(ftok, 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 (_tokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) { if (!unknown) varInfo->clear(); else if (!_settings->library.isLeakIgnore(functionName) && !_settings->library.isUse(functionName)) varInfo->possibleUsageAll(functionName); } } continue; } // delete else if (_tokenizer->isCPP() && tok->str() == "delete") { bool arrayDelete = (tok->strAt(1) == "["); if (arrayDelete) tok = tok->tokAt(3); else 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) != "[") { VarInfo::AllocInfo allocation(arrayDelete ? -2 : -1, VarInfo::DEALLOC); changeAllocStatus(varInfo, allocation, tok, tok); } } // goto => weird execution path else if (tok->str() == "goto") { varInfo->clear(); } // continue/break else if (Token::Match(tok, "continue|break ;")) { varInfo->clear(); } } } void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg) { std::map &alloctype = varInfo->alloctype; std::map &possibleUsage = varInfo->possibleUsage; const std::map::iterator var = alloctype.find(arg->varId()); if (var != alloctype.end()) { if (allocation.status == VarInfo::NOALLOC) { // possible usage possibleUsage[arg->varId()] = tok->str(); if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&") varInfo->erase(arg->varId()); } else if (var->second.status == VarInfo::DEALLOC) { doubleFreeError(tok, arg->str(), allocation.type); } else if (var->second.type != allocation.type) { // mismatching allocation and deallocation mismatchError(tok, arg->str()); varInfo->erase(arg->varId()); } else { // deallocation var->second.status = VarInfo::DEALLOC; var->second.type = allocation.type; } } else if (allocation.status != VarInfo::NOALLOC) { alloctype[arg->varId()].status = VarInfo::DEALLOC; } } void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) { // Ignore function call? if (_settings->library.isLeakIgnore(tok->str())) return; int argNr = 1; for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) { if (_tokenizer->isCPP() && arg->str() == "new") { arg = arg->next(); if (Token::simpleMatch(arg, "( std :: nothrow )")) arg = arg->tokAt(5); } while (Token::Match(arg, "%var% . %var%")) arg = arg->tokAt(2); if (Token::Match(arg, "%var% [-,)] !!.") || Token::Match(arg, "& %var%")) { // goto variable if (arg->str() == "&") arg = arg->next(); bool isnull = arg->hasKnownIntValue() && arg->values().front().intvalue == 0; // Is variable allocated? if (!isnull && (!af || af->arg == argNr)) changeAllocStatus(varInfo, allocation, tok, arg); } else if (Token::Match(arg, "%name% (")) { const Library::AllocFunc* allocFunc = _settings->library.dealloc(arg); VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC); if (alloc.type == 0) alloc.status = VarInfo::NOALLOC; functionCall(arg, varInfo, alloc, allocFunc); } 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::DEALLOC) { 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 = _tokenizer->getSymbolDatabase(); for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) { // don't warn if variable is conditionally allocated if (it->second.status != VarInfo::DEALLOC && 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 unsigned 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|(|, %varid% [);,]", varid)) { used = true; break; } if (Token::Match(tok2, "return|(|, & %varid% . %name% [);,]", varid)) { used = true; break; } } // return deallocated pointer if (used && it->second.status == VarInfo::DEALLOC) deallocReturnError(tok, var->name()); else if (!used && it->second.status != VarInfo::DEALLOC) { 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.82/lib/checkleakautovar.h000066400000000000000000000117701322667425100174750ustar00rootroot00000000000000/* * 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 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 { DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; struct AllocInfo { AllocStatus status; int type; AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC) : status(status_), type(type_) {} }; std::map alloctype; std::map possibleUsage; std::set conditionalAlloc; std::set referenced; void clear() { alloctype.clear(); possibleUsage.clear(); conditionalAlloc.clear(); referenced.clear(); } void erase(unsigned 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) { } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { 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); /** parse function call */ void functionCall(const Token *tok, 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); /** 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* tok, const std::string &varname); void deallocUseError(const Token *tok, const std::string &varname); void deallocReturnError(const Token *tok, const std::string &varname); void doubleFreeError(const Token *tok, 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 { CheckLeakAutoVar c(nullptr, settings, errorLogger); c.deallocReturnError(nullptr, "p"); c.configurationInfo(nullptr, "f"); // user configuration is needed to complete analysis c.doubleFreeError(nullptr, "varname", 0); } static std::string myName() { return "Leaks (auto variables)"; } std::string classInfo() const { return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkleakautovarH cppcheck-1.82/lib/checkmemoryleak.cpp000066400000000000000000003254501322667425100176620ustar00rootroot00000000000000/* * 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 "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 /** * Count function parameters * \param tok Function name token before the '(' */ static unsigned int countParameters(const Token *tok) { tok = tok->tokAt(2); if (tok->str() == ")") return 0; unsigned int numpar = 1; while (nullptr != (tok = tok->nextArgument())) numpar++; return numpar; } /** 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 = make_container < std::set > () << "_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"; //--------------------------------------------------------------------------- bool CheckMemoryLeak::isclass(const Token *tok, unsigned int varid) const { if (tok->isStandardType()) return false; const Variable * var = tokenizer->getSymbolDatabase()->getVariableFromVarId(varid); // 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 && var->typeScope() && var->typeScope()->numConstructors == 0 && (var->typeScope()->varlist.empty() || var->type()->needInitialization == Type::True) && var->type()->derivedFrom.empty()) return false; return true; } //--------------------------------------------------------------------------- CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, unsigned 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.. if (varid && Token::Match(tok2, "realloc ( %any% ,") && tok2->tokAt(2)->varId() != varid) return Malloc; if (tokenizer->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; return New; } if (settings1->standards.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 unsigned int num = countParameters(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 = settings1->library.alloc(tok2, -1); if (alloctype > 0) { if (alloctype == settings1->library.deallocId("free")) return Malloc; if (alloctype == settings1->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, unsigned int varid) { // What we may have... // * var = (char *)realloc(..; if (tok2 && tok2->str() == "(") { tok2 = tok2->link(); tok2 = tok2 ? tok2->next() : nullptr; } if (! tok2) return No; if (varid > 0 && ! Token::Match(tok2, "%name% ( %varid% [,)]", varid)) return No; if (tok2->str() == "realloc") return Malloc; return No; } CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, unsigned int varid) const { if (tokenizer->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 (settings1->standards.posix) { if (tok->str() == "close") return Fd; if (tok->str() == "pclose") return Pipe; } // Does tok point on a Library deallocation function? const int dealloctype = settings1->library.dealloc(tok, argNr); if (dealloctype > 0) { if (dealloctype == settings1->library.deallocId("free")) return Malloc; if (dealloctype == settings1->library.deallocId("fclose")) return File; return Library::ismemory(dealloctype) ? OtherMem : OtherRes; } } argNr++; } } return No; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- 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, tokenizer ? &tokenizer->list : nullptr, severity, id, msg, cwe, false); if (errorLogger) errorLogger->reportErr(errmsg); else Check::reportError(errmsg); } void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "memleak", "Memory leak: " + varname, CWE(401U)); } void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "memleakOnRealloc", "Common realloc mistake: \'" + varname + "\' 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 += ": " + varname; reportErr(tok, Severity::error, "resourceLeak", errmsg, CWE(775U)); } void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocDealloc", "Deallocating a deallocated pointer: " + varname, CWE(415U)); } void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocuse", "Dereferencing '" + varname + "' 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", "Mismatching allocation and deallocation: " + varname, CWE(762U)); } CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list *callstack) const { if (!func || !func->hasBody()) return No; // Get return pointer.. unsigned int varid = 0; for (const Token *tok2 = func->functionScope->classStart; tok2 != func->functionScope->classEnd; tok2 = tok2->next()) { if (tok2->str() == "return") { 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 = tokenizer->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->classStart; tok != func->functionScope->classEnd; 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 (!tokenizer->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, unsigned 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->classStart; tok && tok != func->functionScope->classEnd; 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) { allocType = getReallocationType(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, unsigned 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, unsigned 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); } static bool alwaysTrue(const Token *tok) { if (!tok) return false; if (tok->values().size() == 1U && tok->values().front().isKnown() && tok->values().front().intvalue != 0) return true; if (tok->str() == "||") return alwaysTrue(tok->astOperand1()) || alwaysTrue(tok->astOperand2()); if (tok->str() == "true") return true; return false; } 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")); } namespace { const std::set call_func_keywords = make_container < std::set > () << "asprintf" << "delete" << "fclose" << "for" << "free" << "if" << "realloc" << "return" << "switch" << "while" << "sizeof"; } const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &allocpar, unsigned int sz) { if (test_white_list(tok->str(), _settings, tokenizer->isCPP())) { if (call_func_keywords.find(tok->str())!=call_func_keywords.end()) return nullptr; // is the varid a parameter? for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != tok->linkAt(1); tok2 = tok2->next()) { if (tok2->str() == "(") { tok2 = tok2->nextArgument(); if (!tok2) break; } if (tok2->varId() == varid) { if (tok->strAt(-1) == ".") return "use"; else if (tok2->strAt(1) == "=") return "assign"; else if (tok->str()=="printf") return "use"; // <- it is not certain printf dereference the pointer TODO: check the format string else return "use_"; } } return nullptr; } if (_settings->library.isnoreturn(tok) && tok->strAt(-1) != "=") return "exit"; if (varid > 0 && (getReallocationType(tok, varid) != No || getDeallocationType(tok, varid) != No)) return nullptr; if (callstack.size() > 2) return "dealloc_"; const std::string& funcname(tok->str()); for (std::list::const_iterator it = callstack.begin(); it != callstack.end(); ++it) { if ((*it) && (*it)->str() == funcname) return "recursive"; } callstack.push_back(tok); // lock/unlock.. if (varid == 0) { const Function* func = tok->function(); if (!func || !func->hasBody()) return nullptr; Token *ftok = getcode(func->functionScope->classStart->next(), callstack, 0, alloctype, dealloctype, false, 1); simplifycode(ftok); const char *ret = nullptr; if (Token::simpleMatch(ftok, "; alloc ; }")) ret = "alloc"; else if (Token::simpleMatch(ftok, "; dealloc ; }")) ret = "dealloc"; TokenList::deleteTokens(ftok); return ret; } // how many parameters is there in the function call? const unsigned int numpar = countParameters(tok); if (numpar == 0) { // Taking return value => it is not a noreturn function if (tok->strAt(-1) == "=") return nullptr; // Function is not noreturn if (tok->function() && tok->function()->functionScope) { std::string temp; if (!_settings->library.isScopeNoReturn(tok->function()->functionScope->classEnd, &temp) && temp.empty()) return nullptr; } else if (_settings->library.isnotnoreturn(tok)) return nullptr; return "callfunc"; } unsigned int par = 0; const bool dot(tok->previous()->str() == "."); const bool eq(tok->previous()->str() == "="); const Token *functok = tok; tok = Token::findsimplematch(tok, "("); if (tok) tok = tok->next(); for (; tok; tok = tok->nextArgument()) { ++par; if (Token::Match(tok, "%varid% [,()]", varid)) { if (dot) return "use"; const Function* function = functok->function(); if (!function) return "use"; // how many parameters does the function want? if (numpar != function->argCount()) // TODO: Handle default parameters return "recursive"; if (!function->functionScope) return "use"; const Variable* param = function->getArgumentVar(par-1); if (!param || !param->nameToken()) return "use"; Token *func = getcode(function->functionScope->classStart->next(), callstack, param->declarationId(), alloctype, dealloctype, false, sz); //simplifycode(func); const Token *func_ = func; while (func_ && func_->str() == ";") func_ = func_->next(); const char *ret = nullptr; /** @todo handle "goto" */ if (Token::findsimplematch(func_, "dealloc")) ret = "dealloc"; else if (Token::findsimplematch(func_, "use")) ret = "use"; else if (Token::findsimplematch(func_, "&use")) ret = "&use"; TokenList::deleteTokens(func); return ret; } if (Token::Match(tok, "& %varid% [,()]", varid)) { const Function *func = functok->function(); if (func == nullptr) continue; AllocType a; const char *ret = functionArgAlloc(func, par, a); if (a != No) { if (alloctype == No) alloctype = a; else if (alloctype != a) alloctype = Many; allocpar = true; return ret; } } if (Token::Match(tok, "%varid% . %name% [,)]", varid)) return "use"; } return (eq || _settings->experimental) ? nullptr : "callfunc"; } static void addtoken(Token **rettail, const Token *tok, const std::string &str) { (*rettail)->insertToken(str); (*rettail) = (*rettail)->next(); (*rettail)->linenr(tok->linenr()); (*rettail)->fileIndex(tok->fileIndex()); } Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list callstack, const unsigned int varid, CheckMemoryLeak::AllocType &alloctype, CheckMemoryLeak::AllocType &dealloctype, bool classmember, unsigned int sz) { // variables whose value depends on if(!var). If one of these variables // is used in a if-condition then generate "ifv" instead of "if". std::set extravar; // The first token should be ";" Token* rethead = new Token(nullptr); rethead->str(";"); rethead->linenr(tok->linenr()); rethead->fileIndex(tok->fileIndex()); Token* rettail = rethead; int indentlevel = 0; int parlevel = 0; for (; tok; tok = tok->next()) { if (tok->str() == "{") { addtoken(&rettail, tok, "{"); ++indentlevel; } else if (tok->str() == "}") { addtoken(&rettail, tok, "}"); if (indentlevel <= 0) break; --indentlevel; } else if (tok->str() == "(") ++parlevel; else if (tok->str() == ")") --parlevel; if (parlevel == 0 && tok->str() == ";") addtoken(&rettail, tok, ";"); // Start of new statement.. check if the statement has anything interesting if (varid > 0 && parlevel == 0 && Token::Match(tok, "[;{}]")) { if (Token::Match(tok->next(), "[{};]")) continue; // function calls are interesting.. const Token *tok2 = tok; if (Token::Match(tok2, "[{};] :: %name%")) tok2 = tok2->next(); while (Token::Match(tok2->next(), "%name% ::|. %name%")) tok2 = tok2->tokAt(2); if (Token::Match(tok2->next(), "%name% (")) ; else if (Token::Match(tok->next(), "continue|break|return|throw|goto|do|else")) ; else { const Token *skipToToken = nullptr; // scan statement for interesting keywords / varid for (tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { // nothing interesting found => skip this statement skipToToken = tok2->previous(); break; } if (tok2->varId() == varid || tok2->str() == ":" || tok2->str() == "{" || tok2->str() == "}") { break; } } if (skipToToken) { tok = skipToToken; continue; } } } if (varid == 0) { if (!callstack.empty() && Token::Match(tok, "[;{}] __cppcheck_lock|__cppcheck_unlock ( ) ;")) { // Type of leak = Resource leak alloctype = dealloctype = CheckMemoryLeak::File; if (tok->next()->str() == "__cppcheck_lock") { addtoken(&rettail, tok, "alloc"); } else { addtoken(&rettail, tok, "dealloc"); } tok = tok->tokAt(3); continue; } if (Token::simpleMatch(tok, "if (")) { addtoken(&rettail, tok, "if"); tok = tok->next()->link(); continue; } } else { if (Token::Match(tok, "%varid% = close ( %varid% )", varid)) { addtoken(&rettail, tok, "dealloc"); addtoken(&rettail, tok, ";"); addtoken(&rettail, tok, "assign"); addtoken(&rettail, tok, ";"); tok = tok->tokAt(5); continue; } // var = strcpy|.. ( var , if (Token::Match(tok, "[;{}] %varid% = memcpy|memmove|memset|strcpy|strncpy|strcat|strncat ( %varid% ,", varid)) { tok = tok->linkAt(4); continue; } if (Token::Match(tok->previous(), "[(;{}] %varid% =", varid) || Token::Match(tok, "asprintf|vasprintf ( & %varid% ,", varid)) { CheckMemoryLeak::AllocType alloc; if (Token::Match(tok, "asprintf|vasprintf (")) { // todo: check how the return value is used. if (!Token::Match(tok->previous(), "[;{}]")) { TokenList::deleteTokens(rethead); return nullptr; } alloc = Malloc; tok = tok->next()->link(); } else { alloc = getAllocationType(tok->tokAt(2), varid); } if (sz > 1 && Token::Match(tok->tokAt(2), "malloc ( %num% )") && (MathLib::toLongNumber(tok->strAt(4)) % long(sz)) != 0) { mismatchSizeError(tok->tokAt(4), tok->strAt(4)); } if (alloc == CheckMemoryLeak::No) { alloc = getReallocationType(tok->tokAt(2), varid); if (alloc != CheckMemoryLeak::No) { addtoken(&rettail, tok, "realloc"); addtoken(&rettail, tok, ";"); tok = tok->tokAt(2); if (Token::Match(tok, "%name% (")) tok = tok->next()->link(); continue; } } // don't check classes.. if (alloc == CheckMemoryLeak::New) { if (Token::Match(tok->tokAt(2), "new struct| %type% [(;]")) { const int offset = tok->strAt(3) == "struct" ? 1 : 0; if (isclass(tok->tokAt(3 + offset), varid)) { alloc = No; } } else if (Token::Match(tok->tokAt(2), "new ( nothrow ) struct| %type%")) { const int offset = tok->strAt(6) == "struct" ? 1 : 0; if (isclass(tok->tokAt(6 + offset), varid)) { alloc = No; } } else if (Token::Match(tok->tokAt(2), "new ( std :: nothrow ) struct| %type%")) { const int offset = tok->strAt(8) == "struct" ? 1 : 0; if (isclass(tok->tokAt(8 + offset), varid)) { alloc = No; } } if (Token::simpleMatch(tok->next(), "= new")) { tok = tok->tokAt(2); while (Token::Match(tok->next(), "%name%|::|(|[")) { if (Token::Match(tok->next(), "(|[")) tok = tok->linkAt(1); else tok = tok->next(); } } if (alloc == No && alloctype == No) alloctype = CheckMemoryLeak::New; } if (alloc != No) { addtoken(&rettail, tok, "alloc"); if (alloctype != No && alloctype != alloc) alloc = Many; if (alloc != Many && dealloctype != No && dealloctype != Many && dealloctype != alloc) { callstack.push_back(tok); mismatchAllocDealloc(callstack, Token::findmatch(_tokenizer->tokens(), "%varid%", varid)->str()); callstack.pop_back(); } alloctype = alloc; if (Token::Match(tok, "%name% = %type% (")) { tok = tok->linkAt(3); continue; } } // assignment.. else { // is the pointer in rhs? bool rhs = false; bool trailingSemicolon = false; bool used = false; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { trailingSemicolon = true; if (rhs) tok = tok2; break; } if (!used && !rhs) { if (Token::Match(tok2, "[=+(,] %varid%", varid)) { if (Token::Match(tok2, "[(,]")) { used = true; addtoken(&rettail, tok, "use"); addtoken(&rettail, tok, ";"); } rhs = true; } } } if (!used) { if (!rhs) addtoken(&rettail, tok, "assign"); else { addtoken(&rettail, tok, "use_"); if (trailingSemicolon) addtoken(&rettail, tok, ";"); } } continue; } } if (Token::Match(tok->previous(), "%op%|;|{|}|) ::| %name%") || (Token::Match(tok->previous(), "( ::| %name%") && (!rettail || rettail->str() != "loop"))) { if (tok->str() == "::") tok = tok->next(); if (Token::Match(tok, "%varid% ?", varid)) tok = tok->tokAt(2); AllocType dealloc = getDeallocationType(tok, varid); if (dealloc != No && tok->str() == "fcloseall" && alloctype != dealloc) ; else if (dealloc != No) { addtoken(&rettail, tok, "dealloc"); if (dealloctype != No && dealloctype != dealloc) dealloc = Many; if (dealloc != Many && alloctype != No && alloctype != Many && alloctype != dealloc) { callstack.push_back(tok); mismatchAllocDealloc(callstack, Token::findmatch(_tokenizer->tokens(), "%varid%", varid)->str()); callstack.pop_back(); } dealloctype = dealloc; if (tok->strAt(2) == "(") tok = tok->linkAt(2); continue; } } // if else switch if (Token::simpleMatch(tok, "if (")) { if (alloctype == Fd) { if (ifvar(tok, varid, ">", "-1") || ifvar(tok, varid, ">=", "0") || ifvar(tok, varid, ">", "0") || ifvar(tok, varid, "!=", "-1")) { addtoken(&rettail, tok, "if(var)"); tok = tok->next()->link(); continue; } else if (ifvar(tok, varid, "==", "-1") || ifvar(tok, varid, "<", "0")) { addtoken(&rettail, tok, "if(!var)"); tok = tok->next()->link(); continue; } } if (ifvar(tok, varid, "!=", "0")) { addtoken(&rettail, tok, "if(var)"); // Make sure the "use" will not be added tok = tok->next()->link(); continue; } else if (ifvar(tok, varid, "==", "0")) { addtoken(&rettail, tok, "if(!var)"); // parse the if-body. // if a variable is assigned then add variable to "extravar". for (const Token *tok2 = tok->next()->link()->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") tok2 = tok2->link(); else if (tok2->str() == "}") break; else if (Token::Match(tok2, "%var% =")) extravar.insert(tok2->varId()); } tok = tok->next()->link(); continue; } else { // Check if the condition depends on var or extravar somehow.. bool dep = false; const Token* const end = tok->linkAt(1); for (const Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "close|pclose|fclose|closedir ( %varid% )", varid)) { addtoken(&rettail, tok, "dealloc"); addtoken(&rettail, tok, ";"); dep = true; break; } else if (alloctype == Fd && Token::Match(tok2, "%varid% !=|>=", varid)) { dep = true; } else if (Token::Match(tok2, "! %varid%", varid)) { dep = true; } else if (Token::Match(tok2, "%name% (") && !test_white_list(tok2->str(), _settings, tokenizer->isCPP())) { bool use = false; for (const Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->nextArgument()) { if (Token::Match(tok3->previous(), "(|, &| %varid% ,|)", varid)) { use = true; break; } } if (use) { addtoken(&rettail, tok, "use"); addtoken(&rettail, tok, ";"); dep = false; break; } } else if (tok2->varId() && extravar.find(tok2->varId()) != extravar.end()) { dep = true; } else if (tok2->varId() == varid && (tok2->next()->isConstOp() || tok2->previous()->isConstOp())) dep = true; } if (notvar(tok->next()->astOperand2(), varid)) addtoken(&rettail, tok, "if(!var)"); else addtoken(&rettail, tok, (dep ? "ifv" : "if")); tok = tok->next()->link(); continue; } } } if ((tok->str() == "else") || (tok->str() == "switch")) { addtoken(&rettail, tok, tok->str()); if (Token::simpleMatch(tok, "switch (")) tok = tok->next()->link(); continue; } if ((tok->str() == "case")) { addtoken(&rettail, tok, "case"); addtoken(&rettail, tok, ";"); if (Token::Match(tok, "case %any% :")) tok = tok->tokAt(2); continue; } if ((tok->str() == "default")) { addtoken(&rettail, tok, "default"); addtoken(&rettail, tok, ";"); continue; } // Loops.. else if ((tok->str() == "for") || (tok->str() == "while")) { const Token* const end = tok->linkAt(1); if ((Token::simpleMatch(tok, "while (") && alwaysTrue(tok->next()->astOperand2())) || Token::simpleMatch(tok, "for ( ; ; )")) { addtoken(&rettail, tok, "while1"); tok = end; continue; } else if (varid && getDeallocationType(tok->tokAt(2), varid) != No) { addtoken(&rettail, tok, "dealloc"); addtoken(&rettail, tok, ";"); } else if (alloctype == Fd && varid) { if (Token::Match(tok, "while ( 0 <= %varid% )", varid) || Token::Match(tok, "while ( %varid% >= 0 )", varid) || Token::Match(tok, "while ( %varid% != -1 )", varid) || Token::Match(tok, "while ( -1 != %varid% )", varid)) { addtoken(&rettail, tok, "while(var)"); tok = end; continue; } else if (Token::Match(tok, "while ( %varid% == -1 )", varid) || Token::Match(tok, "while ( -1 == %varid% )", varid) || Token::Match(tok, "while ( %varid% < 0 )", varid) || Token::Match(tok, "while ( 0 > %varid% )", varid)) { addtoken(&rettail, tok, "while(!var)"); tok = end; continue; } } else if (varid && Token::Match(tok, "while ( %varid% )", varid)) { addtoken(&rettail, tok, "while(var)"); tok = end; continue; } else if (varid && Token::simpleMatch(tok, "while (") && notvar(tok->next()->astOperand2(), varid)) { addtoken(&rettail, tok, "while(!var)"); tok = end; continue; } addtoken(&rettail, tok, "loop"); if (varid > 0 && notvar(tok->next()->astOperand2(), varid)) addtoken(&rettail, tok, "!var"); continue; } if ((tok->str() == "do")) { addtoken(&rettail, tok, "do"); continue; } // continue / break.. else if (tok->str() == "continue") { addtoken(&rettail, tok, "continue"); } else if (tok->str() == "break") { addtoken(&rettail, tok, "break"); } else if (tok->str() == "goto") { addtoken(&rettail, tok, "goto"); } // Return.. else if (tok->str() == "return") { addtoken(&rettail, tok, "return"); if (varid == 0) { addtoken(&rettail, tok, ";"); while (tok && tok->str() != ";") tok = tok->next(); if (!tok) break; continue; } // Returning a auto_ptr of this allocated variable.. if (Token::simpleMatch(tok->next(), "std :: auto_ptr <")) { const Token *tok2 = tok->linkAt(4); if (Token::Match(tok2, "> ( %varid% )", varid)) { addtoken(&rettail, tok, "use"); tok = tok2->tokAt(3); } } else if (varid && Token::Match(tok, "return strcpy|strncpy|memcpy ( %varid%", varid)) { addtoken(&rettail, tok, "use"); tok = tok->tokAt(2); } else { bool use = false; std::stack functions; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { tok = tok2; break; } if (tok2->str() == "(") functions.push(tok2->previous()); else if (!functions.empty() && tok2->str() == ")") functions.pop(); if (tok2->varId() == varid) { // Read data.. if (!Token::Match(tok2->previous(), "&|(") && tok2->strAt(1) == "[") { ; } else if (functions.empty() || !test_white_list(functions.top()->str(), _settings, tokenizer->isCPP()) || getDeallocationType(functions.top(),varid) != AllocType::No) { use = true; } } } if (use) addtoken(&rettail, tok, "use"); addtoken(&rettail, tok, ";"); } } // throw.. else if (tokenizer->isCPP() && Token::Match(tok, "try|throw|catch")) { addtoken(&rettail, tok, tok->str()); if (tok->strAt(1) == "(") tok = tok->next()->link(); } // Assignment.. if (varid) { if (Token::simpleMatch(tok, "= {")) { const Token* const end2 = tok->linkAt(1); bool use = false; for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { if (tok2->varId() == varid) { use = true; break; } } if (use) { addtoken(&rettail, tok, "use"); addtoken(&rettail, tok, ";"); tok = tok->next()->link(); continue; } } if (Token::Match(tok, "& %name% = %varid% ;", varid)) { while (rethead->next()) rethead->deleteNext(); return rethead; } if (Token::Match(tok, "[)=] %varid% [+;)]", varid) || (Token::Match(tok, "%name% + %varid%", varid) && tok->strAt(3) != "[" && tok->strAt(3) != ".") || Token::Match(tok, "<< %varid% ;", varid) || Token::Match(tok, "= strcpy|strcat|memmove|memcpy ( %varid% ,", varid) || Token::Match(tok, "[;{}] %name% [ %varid% ]", varid)) { addtoken(&rettail, tok, "use"); } else if (Token::Match(tok->previous(), ";|{|}|=|(|,|%cop% %varid% .|[", varid)) { // warning is written for "dealloc ; use_ ;". // but this use doesn't affect the leak-checking addtoken(&rettail, tok, "use_"); } } // Investigate function calls.. if (Token::Match(tok, "%name% (")) { // A function call should normally be followed by ";" if (Token::simpleMatch(tok->next()->link(), ") {")) { if (!Token::Match(tok, "if|for|while|switch")) { addtoken(&rettail, tok, "exit"); addtoken(&rettail, tok, ";"); tok = tok->next()->link(); continue; } } // Calling setjmp / longjmp => bail out else if (Token::Match(tok, "setjmp|longjmp")) { while (rethead->next()) rethead->deleteNext(); return rethead; } // Inside class function.. if the var is passed as a parameter then // just add a "::use" // The "::use" means that a member function was probably called but it wasn't analysed further else if (classmember) { if (_settings->library.isnoreturn(tok)) addtoken(&rettail, tok, "exit"); else if (!test_white_list(tok->str(), _settings, tokenizer->isCPP())) { const Token* const end2 = tok->linkAt(1); for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { if (tok2->varId() == varid) { addtoken(&rettail, tok, "::use"); break; } } } } else { if (varid > 0 && Token::Match(tok, "%name% ( close|fclose|pclose ( %varid% ) ) ;", varid)) { addtoken(&rettail, tok, "dealloc"); tok = tok->next()->link(); continue; } bool allocpar = false; const char *str = call_func(tok, callstack, varid, alloctype, dealloctype, allocpar, sz); if (str) { if (allocpar) { addtoken(&rettail, tok, str); tok = tok->next()->link(); } else if (varid == 0 || str != std::string("alloc")) { addtoken(&rettail, tok, str); } else if (Token::Match(tok->tokAt(-2), "%varid% =", varid)) { addtoken(&rettail, tok, str); } } else if (varid > 0 && getReallocationType(tok, varid) != No && tok->tokAt(2)->varId() == varid) { addtoken(&rettail, tok, "if"); addtoken(&rettail, tok, "{"); addtoken(&rettail, tok, "dealloc"); addtoken(&rettail, tok, ";"); addtoken(&rettail, tok, "}"); tok = tok->next()->link(); continue; } } } // Callback.. if (Token::Match(tok, "( *| %name%") && Token::simpleMatch(tok->link(),") (")) { const Token *tok2 = tok->next(); if (tok2->str() == "*") tok2 = tok2->next(); tok2 = tok2->next(); while (Token::Match(tok2, ". %name%")) tok2 = tok2->tokAt(2); if (Token::simpleMatch(tok2, ") (")) { for (; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{]")) break; else if (tok2->varId() == varid) { addtoken(&rettail, tok, "use"); break; } } } } // Linux lists.. if (varid > 0 && Token::Match(tok, "[=(,] & (| %varid% [.[,)]", varid)) { // Is variable passed to a "leak-ignore" function? bool leakignore = false; if (Token::Match(tok, "[(,]")) { const Token *parent = tok; while (parent && parent->str() != "(") parent = parent->astParent(); if (parent && parent->astOperand1() && parent->astOperand1()->isName()) { const std::string &functionName = parent->astOperand1()->str(); if (_settings->library.isLeakIgnore(functionName)) leakignore = true; } } // Not passed to "leak-ignore" function, add "&use". if (!leakignore) addtoken(&rettail, tok, "&use"); } } for (Token *tok1 = rethead; tok1; tok1 = tok1->next()) { if (Token::simpleMatch(tok1, "callfunc alloc ;")) { tok1->deleteThis(); tok1->insertToken("use"); tok1->insertToken(";"); } } return rethead; } void CheckMemoryLeakInFunction::simplifycode(Token *tok) const { if (_tokenizer->isCPP()) { // Replace "throw" that is not in a try block with "return" int indentlevel = 0; int trylevel = -1; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { --indentlevel; if (indentlevel <= trylevel) trylevel = -1; } else if (trylevel == -1 && tok2->str() == "try") trylevel = indentlevel; else if (trylevel == -1 && tok2->str() == "throw") tok2->str("return"); } } const bool printExperimental = _settings->experimental; // Insert extra ";" for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (!tok2->previous() || Token::Match(tok2->previous(), "[;{}]")) { if (Token::Match(tok2, "assign|callfunc|use assign|callfunc|use|}")) { tok2->insertToken(";"); } } } // remove redundant braces.. for (Token *start = tok; start; start = start->next()) { if (Token::simpleMatch(start, "; {")) { // the "link" doesn't work here. Find the end brace.. unsigned int indent = 0; for (Token *end = start; end; end = end->next()) { if (end->str() == "{") ++indent; else if (end->str() == "}") { if (indent <= 1) { // If the start/end braces are redundant, delete them if (indent == 1 && Token::Match(end->previous(), "[;{}] } %any%")) { start->deleteNext(); end->deleteThis(); } break; } --indent; } } } } // reduce the code.. // it will be reduced in N passes. When a pass completes without any // simplifications the loop is done. bool done = false; while (! done) { //tok->printOut("simplifycode loop.."); done = true; // reduce callfunc for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "callfunc") { if (!Token::Match(tok2->previous(), "[;{}] callfunc ; }")) tok2->deleteThis(); } } // If the code starts with "if return ;" then remove it if (Token::Match(tok, ";| if return ;")) { tok->deleteNext(); tok->deleteThis(); if (tok->str() == "return") tok->deleteThis(); if (tok->strAt(1) == "else") tok->deleteNext(); } // simplify "while1" contents.. for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "while1 {")) { unsigned int innerIndentlevel = 0; for (Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++innerIndentlevel; else if (tok3->str() == "}") { if (innerIndentlevel == 0) break; --innerIndentlevel; } while (innerIndentlevel == 0 && Token::Match(tok3, "[{};] if|ifv|else { continue ; }")) { tok3->deleteNext(5); if (tok3->strAt(1) == "else") tok3->deleteNext(); } } if (Token::simpleMatch(tok2, "while1 { if { dealloc ; return ; } }")) { tok2->str(";"); tok2->deleteNext(3); tok2->tokAt(4)->deleteNext(2); } } } // Main inner simplification loop for (Token *tok2 = tok; tok2; tok2 = tok2 ? tok2->next() : nullptr) { // Delete extra ";" while (Token::Match(tok2, "[;{}] ;")) { tok2->deleteNext(); done = false; } // Replace "{ }" with ";" if (Token::simpleMatch(tok2->next(), "{ }")) { tok2->deleteNext(2); tok2->insertToken(";"); done = false; } // Delete braces around a single instruction.. if (Token::Match(tok2->next(), "{ %name% ; }")) { tok2->deleteNext(); tok2->tokAt(2)->deleteNext(); done = false; } if (Token::Match(tok2->next(), "{ %name% %name% ; }")) { tok2->deleteNext(); tok2->tokAt(3)->deleteNext(); done = false; } // Reduce "if if|callfunc" => "if" else if (Token::Match(tok2, "if if|callfunc")) { tok2->deleteNext(); done = false; } // outer/inner if blocks. Remove outer condition.. else if (Token::Match(tok2->next(), "if|if(var) { if return use ; }")) { tok2->deleteNext(2); tok2->tokAt(4)->deleteNext(); done = false; } else if (tok2->next() && tok2->next()->str() == "if") { // Delete empty if that is not followed by an else if (Token::Match(tok2->next(), "if ; !!else")) { tok2->deleteNext(); done = false; } // Reduce "if X ; else X ;" => "X ;" else if (Token::Match(tok2->next(), "if %name% ; else %name% ;") && tok2->strAt(2) == tok2->strAt(5)) { tok2->deleteNext(4); done = false; } // Reduce "if continue ; if continue ;" => "if continue ;" else if (Token::simpleMatch(tok2->next(), "if continue ; if continue ;")) { tok2->deleteNext(3); done = false; } // Reduce "if return ; alloc ;" => "alloc ;" else if (Token::Match(tok2, "[;{}] if return ; alloc|return ;")) { tok2->deleteNext(3); done = false; } // "[;{}] if alloc ; else return ;" => "[;{}] alloc ;" else if (Token::Match(tok2, "[;{}] if alloc ; else return ;")) { // Remove "if" tok2->deleteNext(); // Remove "; else return" tok2->next()->deleteNext(3); done = false; } // Reduce "if ; else %name% ;" => "if %name% ;" else if (Token::Match(tok2->next(), "if ; else %name% ;")) { tok2->next()->deleteNext(2); done = false; } // Reduce "if ; else" => "if" else if (Token::simpleMatch(tok2->next(), "if ; else")) { tok2->next()->deleteNext(2); done = false; } // Reduce "if return ; else|if return|continue ;" => "if return ;" else if (Token::Match(tok2->next(), "if return ; else|if return|continue|break ;")) { tok2->tokAt(3)->deleteNext(3); done = false; } // Reduce "if continue|break ; else|if return ;" => "if return ;" else if (Token::Match(tok2->next(), "if continue|break ; if|else return ;")) { tok2->next()->deleteNext(3); done = false; } // Remove "else" after "if continue|break|return" else if (Token::Match(tok2->next(), "if continue|break|return ; else")) { tok2->tokAt(3)->deleteNext(); done = false; } // Delete "if { dealloc|assign|use ; return ; }" else if (Token::Match(tok2, "[;{}] if { dealloc|assign|use ; return ; }") && !Token::findmatch(tok, "if {| alloc ;")) { tok2->deleteNext(7); if (tok2->strAt(1) == "else") tok2->deleteNext(); done = false; } // Remove "if { dealloc ; callfunc ; } !!else|return" else if (Token::Match(tok2->next(), "if { dealloc|assign ; callfunc ; }") && !Token::Match(tok2->tokAt(8), "else|return")) { tok2->deleteNext(7); done = false; } continue; } // Reduce "alloc while(!var) alloc ;" => "alloc ;" if (Token::Match(tok2, "[;{}] alloc ; while(!var) alloc ;")) { tok2->deleteNext(3); done = false; } // Reduce "ifv return;" => "if return use;" if (Token::simpleMatch(tok2, "ifv return ;")) { tok2->str("if"); tok2->next()->insertToken("use"); done = false; } // Reduce "if(var) dealloc ;" and "if(var) use ;" that is not followed by an else.. if (Token::Match(tok2, "[;{}] if(var) assign|dealloc|use ; !!else")) { tok2->deleteNext(); done = false; } // Reduce "; if(!var) alloc ; !!else" => "; dealloc ; alloc ;" if (Token::Match(tok2, "; if(!var) alloc ; !!else")) { // Remove the "if(!var)" tok2->deleteNext(); // Insert "dealloc ;" before the "alloc ;" tok2->insertToken(";"); tok2->insertToken("dealloc"); done = false; } // Reduce "if(!var) exit ;" => ";" if (Token::simpleMatch(tok2, "; if(!var) exit ;")) { tok2->deleteNext(2); done = false; } // Reduce "if* ;".. if (Token::Match(tok2->next(), "if(var)|if(!var)|ifv ;")) { // Followed by else.. if (tok2->strAt(3) == "else") { tok2 = tok2->next(); if (tok2->str() == "if(var)") tok2->str("if(!var)"); else if (tok2->str() == "if(!var)") tok2->str("if(var)"); // remove the "; else" tok2->deleteNext(2); } else { // remove the "if*" tok2->deleteNext(); } done = false; } // Reduce "else ;" => ";" if (Token::simpleMatch(tok2->next(), "else ;")) { tok2->deleteNext(); done = false; } // Reduce "while1 continue| ;" => "use ;" if (Token::Match(tok2, "while1 if| continue| ;")) { tok2->str("use"); while (tok2->strAt(1) != ";") tok2->deleteNext(); done = false; } // Reduce "while1 if break ;" => ";" if (Token::simpleMatch(tok2, "while1 if break ;")) { tok2->str(";"); tok2->deleteNext(2); done = false; } // Delete if block: "alloc; if return use ;" if (Token::Match(tok2, "alloc ; if return use ; !!else")) { tok2->deleteNext(4); done = false; } // Reduce "alloc|dealloc|use|callfunc ; exit ;" => "; exit ;" if (Token::Match(tok2, "[;{}] alloc|dealloc|use|callfunc ; exit ;")) { tok2->deleteNext(); done = false; } // Reduce "alloc|dealloc|use ; if(var) exit ;" if (Token::Match(tok2, "alloc|dealloc|use ; if(var) exit ;")) { tok2->deleteThis(); done = false; } // Remove "if exit ;" if (Token::simpleMatch(tok2, "if exit ;")) { tok2->deleteNext(); tok2->deleteThis(); done = false; } // Remove the "if break|continue ;" that follows "dealloc ; alloc ;" if (!printExperimental && Token::Match(tok2, "dealloc ; alloc ; if break|continue ;")) { tok2->tokAt(3)->deleteNext(2); done = false; } // if break ; break ; => break ; if (Token::Match(tok2->previous(), "[;{}] if break ; break ;")) { tok2->deleteNext(3); done = false; } // Reduce "do { dealloc ; alloc ; } while(var) ;" => ";" if (Token::simpleMatch(tok2->next(), "do { dealloc ; alloc ; } while(var) ;")) { tok2->deleteNext(8); done = false; } // Ticket #7745 // Delete "if (!var) { alloc ; dealloc }" blocks if (Token::simpleMatch(tok2->next(), "if(!var) { alloc ; dealloc ; }")) { tok2->deleteNext(7); done = false; } // Reduce "do { alloc ; } " => "alloc ;" /** @todo If the loop "do { alloc ; }" can be executed twice, reduce it to "loop alloc ;" */ if (Token::simpleMatch(tok2->next(), "do { alloc ; }")) { tok2->deleteNext(2); tok2->tokAt(2)->deleteNext(); done = false; } // Reduce "loop break ; => ";" if (Token::Match(tok2->next(), "loop break|continue ;")) { tok2->deleteNext(2); done = false; } // Reduce "loop|do ;" => ";" if (Token::Match(tok2, "loop|do ;")) { tok2->deleteThis(); done = false; } // Reduce "loop if break|continue ; !!else" => ";" if (Token::Match(tok2->next(), "loop if break|continue ; !!else")) { tok2->deleteNext(3); done = false; } // Reduce "loop { if break|continue ; !!else" => "loop {" if (Token::Match(tok2, "loop { if break|continue ; !!else")) { tok2->next()->deleteNext(3); done = false; } // Replace "do ; loop ;" with ";" if (Token::simpleMatch(tok2, "; loop ;")) { tok2->deleteNext(2); done = false; } // Replace "loop loop .." with "loop .." if (Token::simpleMatch(tok2, "loop loop")) { tok2->deleteThis(); done = false; } // Replace "loop if return ;" with "if return ;" if (Token::simpleMatch(tok2->next(), "loop if return")) { tok2->deleteNext(); done = false; } // Reduce "loop|while1 { dealloc ; alloc ; }" if (Token::Match(tok2, "loop|while1 { dealloc ; alloc ; }")) { // delete "{" tok2->deleteNext(); // delete "loop|while1" tok2->deleteThis(); // delete "}" tok2->tokAt(3)->deleteNext(); done = false; } // loop { use ; callfunc ; } => use ; // assume that the "callfunc" is not noreturn if (Token::simpleMatch(tok2, "loop { use ; callfunc ; }")) { tok2->deleteNext(6); tok2->str("use"); tok2->insertToken(";"); done = false; } // Delete if block in "alloc ; if(!var) return ;" if (Token::simpleMatch(tok2, "alloc ; if(!var) return ;")) { tok2->deleteNext(3); done = false; } // Reduce "[;{}] return use ; %name%" => "[;{}] return use ;" if (Token::Match(tok2, "[;{}] return use ; %name%")) { tok2->tokAt(3)->deleteNext(); done = false; } // Reduce "if(var) return use ;" => "return use ;" if (Token::Match(tok2->next(), "if(var) return use ; !!else")) { tok2->deleteNext(); done = false; } // malloc - realloc => alloc ; dealloc ; alloc ; // Reduce "[;{}] alloc ; dealloc ; alloc ;" => "[;{}] alloc ;" if (Token::Match(tok2, "[;{}] alloc ; dealloc ; alloc ;")) { tok2->deleteNext(4); done = false; } // use; dealloc; => dealloc; if (Token::Match(tok2, "[;{}] use ; dealloc ;")) { tok2->deleteNext(2); done = false; } // use use => use while (Token::simpleMatch(tok2, "use use")) { tok2->deleteNext(); done = false; } // use use_ => use if (Token::simpleMatch(tok2, "use use_")) { tok2->deleteNext(); done = false; } // use_ use => use if (Token::simpleMatch(tok2, "use_ use")) { tok2->deleteThis(); done = false; } // use & use => use while (Token::simpleMatch(tok2, "use & use")) { tok2->deleteNext(2); done = false; } // & use use => use while (Token::simpleMatch(tok2, "& use use")) { tok2->deleteThis(); tok2->deleteThis(); done = false; } // use; if| use; => use; while (Token::Match(tok2, "[;{}] use ; if| use ;")) { Token *t = tok2->tokAt(2); t->deleteNext(2+(t->str()=="if" ? 1 : 0)); done = false; } // Delete first part in "use ; return use ;" if (Token::Match(tok2, "[;{}] use ; return use ;")) { tok2->deleteNext(2); done = false; } // try/catch if (Token::simpleMatch(tok2, "try ; catch exit ;")) { tok2->deleteNext(3); tok2->deleteThis(); done = false; } // Delete second case in "case ; case ;" while (Token::simpleMatch(tok2, "case ; case ;")) { tok2->deleteNext(2); done = false; } // Replace switch with if (if not complicated) if (Token::simpleMatch(tok2, "switch {")) { // Right now, I just handle if there are a few case and perhaps a default. bool valid = false; bool incase = false; for (const Token * _tok = tok2->tokAt(2); _tok; _tok = _tok->next()) { if (_tok->str() == "{") break; else if (_tok->str() == "}") { valid = true; break; } else if (_tok->str() == "switch") break; else if (_tok->str() == "loop") break; else if (incase && _tok->str() == "case") break; else if (Token::Match(_tok, "return !!;")) break; if (Token::Match(_tok, "if return|break use| ;")) _tok = _tok->tokAt(2); incase = incase || (_tok->str() == "case"); incase = incase && (_tok->str() != "break" && _tok->str() != "return"); } if (!incase && valid) { done = false; tok2->str(";"); tok2->deleteNext(); tok2 = tok2->next(); bool first = true; while (Token::Match(tok2, "case|default")) { const bool def(tok2->str() == "default"); tok2->str(first ? "if" : "}"); if (first) { first = false; tok2->insertToken("{"); } else { // Insert "else [if] { tok2->insertToken("{"); if (! def) tok2->insertToken("if"); tok2->insertToken("else"); tok2 = tok2->next(); } while (tok2) { if (tok2->str() == "}") break; if (Token::Match(tok2, "break|return ;")) break; if (Token::Match(tok2, "if return|break use| ;")) tok2 = tok2->tokAt(2); else tok2 = tok2->next(); } if (Token::simpleMatch(tok2, "break ;")) { tok2->str(";"); tok2 = tok2->tokAt(2); } else if (tok2 && tok2->str() == "return") { tok2 = tok2->tokAt(2); } } } } } // If "--all" is given, remove all "callfunc".. if (done && printExperimental) { for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "callfunc") { tok2->deleteThis(); done = false; } } } } } const Token *CheckMemoryLeakInFunction::findleak(const Token *tokens) { const Token *result; if (Token::Match(tokens, "alloc ; if|if(var)|ifv break|continue|return ;")) { return tokens->tokAt(3); } if ((result = Token::findsimplematch(tokens, "loop alloc ;")) != nullptr) { return result; } if ((result = Token::findmatch(tokens, "alloc ; if|if(var)|ifv return ;")) != nullptr) { return result->tokAt(3); } if ((result = Token::findmatch(tokens, "alloc ; alloc|assign|return callfunc| ;")) != nullptr) { return result->tokAt(2); } if ((result = Token::findmatch(tokens, "alloc ; loop|while1 {| alloc ;")) != nullptr) { return result->tokAt(3 + (result->strAt(3) == "{")); } if ((result = Token::findsimplematch(tokens, "; alloc ; if assign ;")) != nullptr) { return result->tokAt(4); } if (((result = Token::findsimplematch(tokens, "; alloc ; if dealloc ; }")) != nullptr) || ((result = Token::findsimplematch(tokens, "; alloc ; if dealloc ; return ;")) != nullptr)) { return result->tokAt(6); } if ((result = Token::findsimplematch(tokens, "alloc ; }")) != nullptr) { if (result->tokAt(3) == nullptr) return result->tokAt(2); } // No deallocation / usage => report leak at the last token if (!Token::findmatch(tokens, "dealloc|use")) { const Token *last = tokens; while (last->next()) last = last->next(); // not a leak if exit is called before the end of the function if (!Token::Match(last->tokAt(-2), "exit|callfunc ; }")) return last; } return nullptr; } // Check for memory leaks for a function variable. void CheckMemoryLeakInFunction::checkScope(const Token *startTok, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz) { std::list callstack; AllocType alloctype = No; AllocType dealloctype = No; const Token *result; Token *tok = getcode(startTok, callstack, varid, alloctype, dealloctype, classmember, sz); //tok->printOut((std::string("Checkmemoryleak: getcode result for: ") + varname).c_str()); const bool use_addr = bool(Token::findsimplematch(tok, "&use") != nullptr); // Simplify the code and check if freed memory is used.. for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { while (Token::Match(tok2, "[;{}] ;")) tok2->deleteNext(); } if ((result = Token::findmatch(tok, "[;{}] dealloc ; use_ ;")) != nullptr) { deallocuseError(result->tokAt(3), varname); } // Replace "&use" with "use". Replace "use_" with ";" for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "&use") tok2->str("use"); else if (tok2->str() == "use_") tok2->str(";"); else if (Token::simpleMatch(tok2, "loop use_ {")) tok2->deleteNext(); else if (tok2->str() == "::use") // Some kind of member function usage. Not analyzed very well. tok2->str("use"); else if (tok2->str() == "recursive") tok2->str("use"); else if (tok2->str() == "dealloc_") tok2->str("dealloc"); else if (tok2->str() == "realloc") { tok2->str("dealloc"); tok2->insertToken("alloc"); tok2->insertToken(";"); } } // If the variable is not allocated at all => no memory leak if (Token::findsimplematch(tok, "alloc") == nullptr) { TokenList::deleteTokens(tok); return; } simplifycode(tok); if (_settings->debug && _settings->verbose) { tok->printOut(("Checkmemoryleak: simplifycode result for: " + varname).c_str()); } // If the variable is not allocated at all => no memory leak if (Token::findsimplematch(tok, "alloc") == nullptr) { TokenList::deleteTokens(tok); return; } /** @todo handle "goto" */ if (Token::findsimplematch(tok, "goto")) { TokenList::deleteTokens(tok); return; } if ((result = findleak(tok)) != nullptr) { memoryLeak(result, varname, alloctype); } else if (!use_addr && (result = Token::findsimplematch(tok, "dealloc ; dealloc ;")) != nullptr) { deallocDeallocError(result->tokAt(2), varname); } // detect cases that "simplifycode" don't handle well.. else if (tok && _settings->debugwarnings) { Token *first = tok; while (first && first->str() == ";") first = first->next(); bool noerr = false; noerr = noerr || Token::simpleMatch(first, "alloc ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; dealloc ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; return use ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; use ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; use ; return ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; dealloc ; return ; }"); noerr = noerr || Token::simpleMatch(first, "if alloc ; dealloc ; }"); noerr = noerr || Token::simpleMatch(first, "if alloc ; return use ; }"); noerr = noerr || Token::simpleMatch(first, "if alloc ; use ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; ifv return ; dealloc ; }"); noerr = noerr || Token::simpleMatch(first, "alloc ; if return ; dealloc; }"); // Unhandled case.. if (!noerr) reportError(first, Severity::debug, "debug", "inconclusive leak of " + varname + ": " + tok->stringifyList(false, false, false, false, false, nullptr, nullptr)); } TokenList::deleteTokens(tok); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // 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, unsigned int varid) { const Variable* var = symbolDatabase->getVariableFromVarId(varid); return var && !var->isArgument(); } void CheckMemoryLeakInFunction::checkReallocUsage() { // only check functions const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // Search for the "var = realloc(var, 100" pattern within this function for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->varId() > 0 && Token::Match(tok, "%name% = realloc|g_try_realloc ( %name% ,") && tok->varId() == tok->tokAt(4)->varId() && isNoArgument(symbolDatabase, tok->varId())) { // Check that another copy of the pointer wasn't saved earlier in the function if (Token::findmatch(scope->classStart, "%name% = %varid% ;", tok, tok->varId()) || Token::findmatch(scope->classStart, "[{};] %varid% = %name% [;=]", tok, tok->varId())) continue; const Token* tokEndRealloc = tok->linkAt(3); // 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 && _tokenizer->IsScopeNoReturn(tokEndBrace)) continue; } memleakUponReallocFailureError(tok, tok->str()); } else if (tok->next()->varId() > 0 && (Token::Match(tok, "* %name% = realloc|g_try_realloc ( * %name% ,") && tok->next()->varId() == tok->tokAt(6)->varId()) && isNoArgument(symbolDatabase, tok->next()->varId())) { // Check that another copy of the pointer wasn't saved earlier in the function if (Token::findmatch(scope->classStart, "%name% = * %varid% ;", tok, tok->next()->varId()) || Token::findmatch(scope->classStart, "[{};] * %varid% = %name% [;=]", tok, tok->next()->varId())) continue; const Token* tokEndRealloc = tok->linkAt(4); // Check that the allocation isn't followed immediately by an 'if (!var) { error(); }' that might handle failure if (Token::Match(tokEndRealloc->next(), "; if ( ! * %varid% ) {", tok->next()->varId())) { const Token* tokEndBrace = tokEndRealloc->linkAt(8); if (tokEndBrace && Token::simpleMatch(tokEndBrace->tokAt(-2), ") ;") && Token::Match(tokEndBrace->linkAt(-2)->tokAt(-2), "{|}|; %name% (")) continue; } memleakUponReallocFailureError(tok->next(), tok->strAt(1)); } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Checks for memory leaks inside function.. //--------------------------------------------------------------------------- static bool isInMemberFunc(const Scope* scope) { while (scope->nestedIn && !scope->functionOf) scope = scope->nestedIn; return (scope->functionOf != nullptr); } void CheckMemoryLeakInFunction::check() { // Check locking/unlocking of global resources.. const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (!scope->hasInlineOrLambdaFunction()) checkScope(scope->classStart->next(), emptyString, 0, scope->functionOf != nullptr, 1); } // Check variables.. for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); if (!var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || !var->scope()) continue; if (var->isReference()) continue; if (!var->isPointer() && var->typeStartToken()->str() != "int") continue; // check for known class without implementation (forward declaration) if (var->isPointer() && var->type() && !var->typeScope()) continue; if (var->scope()->hasInlineOrLambdaFunction()) continue; unsigned int sz = _tokenizer->sizeOfType(var->typeStartToken()); if (sz < 1) sz = 1; if (var->isArgument()) checkScope(var->scope()->classStart->next(), var->name(), i, isInMemberFunc(var->scope()), sz); else checkScope(var->nameToken(), var->name(), i, isInMemberFunc(var->scope()), sz); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Checks for memory leaks in classes.. //--------------------------------------------------------------------------- void CheckMemoryLeakInClass::check() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // only check classes and structures const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { 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 unsigned int varid = tokVarname->varId(); const std::string& classname = scope->className; // Check if member variable has been allocated and deallocated.. CheckMemoryLeak::AllocType Alloc = CheckMemoryLeak::No; CheckMemoryLeak::AllocType Dealloc = CheckMemoryLeak::No; bool allocInConstructor = false; bool deallocInDestructor = false; // Inspect member functions std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 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; Dealloc = CheckMemoryLeak::Many; } continue; } bool body = false; const Token *end = func->functionScope->classEnd; for (const Token *tok = func->arg->link(); tok != end; tok = tok->next()) { if (tok == func->functionScope->classStart) 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 (Alloc != No && Alloc != alloc) alloc = CheckMemoryLeak::Many; if (alloc != CheckMemoryLeak::Many && Dealloc != CheckMemoryLeak::No && Dealloc != CheckMemoryLeak::Many && Dealloc != alloc) { std::list callstack; callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } Alloc = 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 (Dealloc != CheckMemoryLeak::No && Dealloc != dealloc) dealloc = CheckMemoryLeak::Many; if (dealloc != CheckMemoryLeak::Many && Alloc != CheckMemoryLeak::No && Alloc != Many && Alloc != dealloc) { std::list callstack; callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } Dealloc = dealloc; } // Function call .. possible deallocation else if (Token::Match(tok->previous(), "[{};] %name% (")) { if (!CheckMemoryLeakInFunction::test_white_list(tok->str(), _settings, tokenizer->isCPP())) { return; } } } } } if (allocInConstructor && !deallocInDestructor) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, Alloc*/); } else if (Alloc != CheckMemoryLeak::No && Dealloc == CheckMemoryLeak::No) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, Alloc*/); } } void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname) { if (!_settings->isEnabled(Settings::STYLE)) return; reportError(tok, Severity::style, "unsafeClassCanLeak", "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 (!_settings->isEnabled(Settings::WARNING)) return; const unsigned int varid = classtok->varId(); // Parse public functions.. // If they allocate member variables, they should also deallocate std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if ((func->type == Function::eFunction || func->type == Function::eOperatorEqual) && func->access == Public && func->hasBody()) { const Token *tok2 = func->functionScope->classStart->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", "Possible leak in public function. The pointer '" + varname + "' is not deallocated before it is allocated.", CWE398, false); } void CheckMemoryLeakStructMember::check() { const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); if (!var || !var->isLocal() || var->isStatic()) continue; if (var->typeEndToken()->isStandardType()) continue; checkStructVariable(var); } } bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable) { const unsigned int declarationId(variable->declarationId()); bool alloc = false; for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->classEnd; 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 (!_tokenizer->isC() && (!variable->typeScope() || variable->typeScope()->getDestructor())) { // For non-C code a destructor might cleanup members return; } // Check struct.. unsigned int indentlevel2 = 0; for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->classEnd; 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 unsigned int structid(variable->declarationId()); const unsigned int structmemberid(tok2->tokAt(2)->varId()); // This struct member is allocated.. check that it is deallocated unsigned 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.. unsigned 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(), _settings, tokenizer->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 = _tokenizer->getSymbolDatabase(); // only check functions const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // 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); // parse the executable scope until tok is reached... for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { // allocating memory in parameter for function call.. if (!(Token::Match(tok, "[(,] %name% (") && Token::Match(tok->linkAt(2), ") [,)]"))) continue; if (getAllocationType(tok->next(), 0) == No) continue; // locate outer function call.. const Token* tok3 = tok; while (tok3 && tok3->astParent() && tok3->str() == ",") tok3 = tok3->astParent(); if (!tok3 || tok3->str() != "(") continue; // Is it a function call.. if (!Token::Match(tok3->tokAt(-2), "!!= %name% (")) continue; const std::string& functionName = tok3->strAt(-1); if ((tokenizer->isCPP() && functionName == "delete") || functionName == "free" || functionName == "fclose" || functionName == "realloc") break; if (CheckMemoryLeakInFunction::test_white_list(functionName, _settings, tokenizer->isCPP())) { functionCallLeak(tok, tok->strAt(1), functionName); break; } } } } //--------------------------------------------------------------------------- // 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->classStart; tok != scope->classEnd; 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; // get ast parent, skip casts const Token *parent = tok->next()->astParent(); while (parent && parent->str() == "(" && !parent->astOperand2()) parent = parent->astParent(); if (!parent || 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 (!_tokenizer->isCPP() || !_settings->inconclusive || !_settings->isEnabled(Settings::WARNING)) return; for (const Token *tok = scope->classStart; tok != scope->classEnd; 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 <") && tok2->next()->link() && Token::Match(tok2->next()->link(), "> ( new %name%")) { pointerType = tok2; } else if (!isNothrow) { if (Token::Match(tok2, "%name% (")) functionCalled = tok2; else if (tok2->isName() && tok2->next()->link() && 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", "Return value of allocation function '" + alloc + "' 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", "Unsafe allocation. If " + funcName + "() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.", CWE401, true); // Inconclusive because funcName may never throw } cppcheck-1.82/lib/checkmemoryleak.h000066400000000000000000000424231322667425100173230ustar00rootroot00000000000000/* * 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 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 { protected: /** For access to the tokens */ const Tokenizer * const tokenizer; private: /** ErrorLogger used to report errors */ ErrorLogger * const errorLogger; /** Enabled standards */ const Settings * const settings1; /** Disable the default constructors */ CheckMemoryLeak(); /** Disable the default constructors */ CheckMemoryLeak(const CheckMemoryLeak &); /** disable assignment operator */ void operator=(const CheckMemoryLeak &); /** * 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(const Tokenizer *t, ErrorLogger *e, const Settings *s) : tokenizer(t), errorLogger(e), settings1(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, unsigned int varid) const; /** * @brief Get type of allocation at given position */ AllocType getAllocationType(const Token *tok2, unsigned int varid, std::list *callstack = nullptr) const; /** * @brief Get type of reallocation at given position */ static AllocType getReallocationType(const Token *tok2, unsigned int varid); /** * @brief Is a typename the name of a class? * @param tok type token * @param varid variable id * @return true if the type name is the name of a class */ bool isclass(const Token *tok, unsigned int varid) 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 &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, unsigned 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), symbolDatabase(nullptr) { } /** @brief This constructor is used when running checks */ CheckMemoryLeakInFunction(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) { // get the symbol database if (tokenizr) symbolDatabase = tokenizr->getSymbolDatabase(); else symbolDatabase = nullptr; } /** @brief run all simplified checks */ void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) { CheckMemoryLeakInFunction checkMemoryLeak(tokenizr, settings, errLog); checkMemoryLeak.checkReallocUsage(); checkMemoryLeak.check(); } /** @brief Unit testing : testing the white list */ static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp); /** @brief Perform checking */ void check(); /** * Checking for a memory leak caused by improper realloc usage. */ void checkReallocUsage(); /** * Inspect a function call. the call_func and getcode are recursive * @param tok token where the function call occurs * @param callstack callstack * @param varid variable id to check * @param alloctype if memory is allocated, this indicates the type of allocation * @param dealloctype if memory is deallocated, this indicates the type of deallocation * @param allocpar if function allocates varid parameter * @param sz not used by call_func - see getcode * @return These are the possible return values: * - NULL : no significant code * - "recursive" : recursive function * - "alloc" : the function returns allocated memory * - "dealloc" : the function deallocates the variable * - "dealloc_" * - "use" : the variable is used (unknown usage of the variable => the checking bails out) * - "callfunc" : a function call with unknown side effects * - "&use" */ const char * call_func(const Token *tok, std::list callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &allocpar, unsigned int sz); /** * Extract a new tokens list that is easier to parse than the "_tokenizer->tokens()", the * extracted tokens list describes how the given variable is used. * The getcode and call_func are recursive * @param tok start parse token * @param callstack callstack * @param varid variable id * @param alloctype keep track of what type of allocation is used * @param dealloctype keeps track of what type of deallocation is used * @param classmember should be set if the inspected function is a class member * @param sz size of type, used to check for mismatching size of allocation. for example "int *a;" => the sz is "sizeof(int)" * @return Newly allocated token array. Caller needs to release reserved * memory by calling TokenList::deleteTokens(returnValue); * Returned tokens: * - "alloc" : the variable is allocated * - "assign" : the variable is assigned a new value * - "break" : corresponds to "break" * - "callfunc" : a function call with unknown side effects * - "continue" : corresponds to "continue" * - "dealloc" : the variable is deallocated * - "goto" : corresponds to a "goto" * - "if" : there is an "if" * - "if(var)" : corresponds with "if ( var != 0 )" * - "if(!var)" : corresponds with "if ( var == 0 )" * - "ifv" : the variable is used in some way in a "if" * - "loop" : corresponds to either a "for" or a "while" * - "realloc" : the variable is reallocated * - "return" : corresponds to a "return" * - "use" : unknown usage -> bail out checking of this execution path * - "&use" : the address of the variable is taken * - "::use" : calling member function of class * - "use_" : content of variable is accessed (used to warn access after dealloc) */ Token *getcode(const Token *tok, std::list callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool classmember, unsigned int sz); /** * Simplify code e.g. by replacing empty "{ }" with ";" * @param tok first token. The tokens list can be modified. */ void simplifycode(Token *tok) const; static const Token *findleak(const Token *tokens); /** * Checking the variable varname * @param startTok start token * @param varname name of variable (for error messages) * @param varid variable id * @param classmember is the scope inside a class member function * @param sz size of type.. if the variable is a "int *" then sz should be "sizeof(int)" */ void checkScope(const Token *startTok, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz); private: /** Report all possible errors (for the --errorlist) */ void getErrorMessages(ErrorLogger *e, const Settings *settings) const { 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"); std::list callstack; c.mismatchAllocDealloc(callstack, "varname"); c.memleakUponReallocFailureError(nullptr, "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 { return "Is there any allocated memory when a function goes out of scope\n"; } const SymbolDatabase *symbolDatabase; }; /** * @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 *tokenizr, const Settings *settings, ErrorLogger *errLog) : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) { } void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) { 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 { 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 { 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 *tokenizr, const Settings *settings, ErrorLogger *errLog) : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) { } void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) { CheckMemoryLeakStructMember checkMemoryLeak(tokenizr, settings, errLog); 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 { } static std::string myName() { return "Memory leaks (struct members)"; } std::string classInfo() const { 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 *tokenizr, const Settings *settings, ErrorLogger *errLog) : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) { } void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) { CheckMemoryLeakNoVar checkMemoryLeak(tokenizr, settings, errLog); checkMemoryLeak.check(); } void check(); private: /** * @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 { 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 { return "Not taking the address to allocated memory\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkmemoryleakH cppcheck-1.82/lib/checknullpointer.cpp000066400000000000000000000551771322667425100200760ustar00rootroot00000000000000/* * 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 "checknullpointer.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 Token* firstParam = tok.tokAt(2); const Token* secondParam = firstParam->nextArgument(); // 1st parameter.. if (Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && secondParam && secondParam->str() != "0") // Only if length (second parameter) is not zero var.push_back(firstParam); if (library || tok.function() != nullptr) { const Token *param = firstParam; int argnr = 1; while (param) { 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); } param = param->nextArgument(); argnr++; } } if (Token::Match(&tok, "printf|sprintf|snprintf|fprintf|fnprintf|scanf|sscanf|fscanf|wprintf|swprintf|fwprintf|wscanf|swscanf|fwscanf")) { const Token* argListTok = nullptr; // Points to first va_list argument std::string formatString; bool scan = Token::Match(&tok, "scanf|sscanf|fscanf|wscanf|swscanf|fwscanf"); if (Token::Match(&tok, "printf|scanf|wprintf|wscanf ( %str%")) { formatString = firstParam->strValue(); argListTok = secondParam; } else if (Token::Match(&tok, "sprintf|fprintf|sscanf|fscanf|fwprintf|fwscanf|swscanf")) { const Token* formatStringTok = secondParam; // Find second parameter (format string) if (formatStringTok && formatStringTok->tokType() == Token::eString) { argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args) formatString = formatStringTok->strValue(); } } else if (Token::Match(&tok, "snprintf|fnprintf|swprintf") && secondParam) { const Token* formatStringTok = secondParam->nextArgument(); // Find third parameter (format string) if (formatStringTok && formatStringTok->tokType() == Token::eString) { argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args) formatString = formatStringTok->strValue(); } } if (argListTok) { bool percent = false; for (std::string::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 argListTok = argListTok->nextArgument(); } ++i; if (!argListTok || i == formatString.end()) return; } if (_continue) continue; if ((*i == 'n' || *i == 's' || scan)) { var.push_back(argListTok); } if (*i != 'm') // %m is a non-standard glibc extension that requires no parameter argListTok = argListTok->nextArgument(); // Find next argument if (!argListTok) break; } } } } } namespace { const std::set stl_stream = make_container< std::set >() << "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) { unknown = false; const Token* parent = tok->astParent(); if (!parent) return false; if (parent->str() == "." && parent->astOperand2() == tok) return isPointerDeRef(parent, unknown); 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->str() == "*" && !parent->astOperand2() && !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->str() == "&" && !parent2->astOperand2()) return false; // read/write member variable if (firstOperand && parent->str() == "." && (!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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->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 (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { const Token* const tok1 = i->classDef; // search for a "for" scope.. if (i->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 (std::list::const_iterator j = i->nestedList.begin(); j != i->nestedList.end(); ++j) { const Scope* const scope = *j; if (scope->type != Scope::eWhile) continue; // TODO: are there false negatives for "while ( %varid% ||" if (Token::Match(scope->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 = scope->classStart; tok4; tok4 = tok4->next()) { if (tok4 == i->classEnd) { const ValueFlow::Value v(scope->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; } } } } } } } void CheckNullPointer::nullPointerByDeRefAndChec() { const bool printInconclusive = (_settings->inconclusive); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) { tok = tok->next()->link(); continue; } const Variable *var = tok->variable(); if (!var || !var->isPointer() || tok == var->nameToken()) continue; // Can pointer be NULL? const ValueFlow::Value *value = tok->getValue(0); if (!value) continue; if (!printInconclusive && value->isInconclusive()) continue; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %name% [,)]")) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (!ftok || !ftok->previous()) continue; std::list varlist; parseFunctionCall(*ftok->previous(), varlist, &_settings->library); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { nullPointerError(tok, tok->str(), value, value->isInconclusive()); } continue; } // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (unknown) nullPointerError(tok, tok->str(), value, true); continue; } nullPointerError(tok, tok->str(), value, value->isInconclusive()); } } void CheckNullPointer::nullPointer() { nullPointerLinkedList(); nullPointerByDeRefAndChec(); } namespace { const std::set stl_istream = make_container< std::set >() << "fstream" << "ifstream" << "iostream" << "istream" << "istringstream" << "stringstream" << "wistringstream" << "wstringstream"; } /** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; const Token *tok = scope->classStart; if (scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->classEnd; 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, &_settings->library); // is one of the var items a NULL pointer? for (std::list::const_iterator it = var.begin(); it != var.end(); ++it) { if (Token::Match(*it, "0|NULL|nullptr [,)]")) { nullPointerError(*it); } } } } else if (Token::Match(tok, "std :: string|wstring ( 0|NULL|nullptr )")) nullPointerError(tok); 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(ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: " + varname + "."); const std::string errmsgdefarg("Possible null pointer dereference if the default parameter value is used: " + varname); 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 (!_settings->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 += ": " + varname; reportError(errorPath, value->isKnown() ? Severity::error : Severity::warning, "nullPointer", errmsg, CWE476, inconclusive || value->isInconclusive()); } } void CheckNullPointer::arithmetic() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!tok->astOperand2() || tok->str() != "-") continue; // pointer subtraction if (!tok->valueType() || !tok->valueType()->pointer) continue; // Can LHS be NULL? const ValueFlow::Value *value = tok->astOperand1()->getValue(0); if (!value) continue; if (!_settings->inconclusive && value->isInconclusive()) continue; if (value->condition && !_settings->isEnabled(Settings::WARNING)) continue; arithmeticError(tok,value); } } } void CheckNullPointer::arithmeticError(const Token *tok, const ValueFlow::Value *value) { std::string errmsg; if (value && value->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(value->condition) + " or there is overflow in pointer subtraction."; else errmsg = "Overflow in pointer arithmetic, NULL pointer is subtracted."; std::list callstack; callstack.push_back(tok); if (value && value->condition) callstack.push_back(value->condition); reportError(callstack, (value && value->condition) ? Severity::warning : Severity::error, (value && value->condition) ? "nullPointerArithmeticRedundantCheck" : "nullPointerArithmetic", errmsg, CWE682, // unknown - pointer overflow value && value->isInconclusive()); } cppcheck-1.82/lib/checknullpointer.h000066400000000000000000000115211322667425100175240ustar00rootroot00000000000000/* * 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 checknullpointerH #define checknullpointerH //--------------------------------------------------------------------------- #include "check.h" #include "config.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) { CheckNullPointer checkNullPointer(tokenizer, settings, errorLogger); checkNullPointer.nullPointer(); checkNullPointer.arithmetic(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckNullPointer checkNullPointer(tokenizer, settings, errorLogger); 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 */ static bool isPointerDeRef(const Token *tok, bool &unknown); /** @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); private: /** Get error messages. Used by --errorlist */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckNullPointer c(nullptr, settings, errorLogger); c.nullPointerError(nullptr, "pointer", nullptr, false); c.arithmeticError(nullptr, nullptr); } /** Name of check */ static std::string myName() { return "Null pointer"; } /** class info in WIKI format. Used by --doc */ std::string classInfo() const { 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 arithmeticError(const Token *tok, const ValueFlow::Value *value); }; /// @} //--------------------------------------------------------------------------- #endif // checknullpointerH cppcheck-1.82/lib/checkother.cpp000066400000000000000000004152471322667425100166420ustar00rootroot00000000000000/* * 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 "checkother.h" #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 #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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; std::map vars; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 (_tokenizer->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 (_tokenizer->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", "Storing "+ strFunctionName +"() return value in char variable and then comparing with EOF.\n" "When saving "+ strFunctionName +"() return value in char variable there is loss of precision. " " When "+ strFunctionName +"() returns EOF this value is truncated. Comparing the char " "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = "+ strFunctionName +"());\" " "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 (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok && tok != scope->classEnd; 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", "Ineffective statement similar to '*A++;'. 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 (!_settings->inconclusive || !_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); // Look for "if(); {}", "for(); {}" or "while(); {}" for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type == Scope::eIf || i->type == Scope::eElse || i->type == Scope::eWhile || i->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(i->classStart, "{ ; } {") && i->classStart->previous()->linenr() == i->classStart->tokAt(2)->linenr() && i->classStart->linenr()+1 >= i->classStart->tokAt(3)->linenr()) { SuspiciousSemicolonError(i->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 (!_settings->isEnabled(Settings::STYLE) || !_tokenizer->isCPP()) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; const Token* tok; if (scope->function && scope->function->isConstructor()) tok = scope->classDef; else tok = scope->classStart; for (; tok && tok != scope->classEnd; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const| %type% * const| ) (| %name%|%num%|%bool%|%char%|%str%")) continue; // skip first "const" in "const Type* const" if (tok->strAt(1) == "const") 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 (!_settings->isEnabled(Settings::PORTABILITY)) return; const bool printInconclusive = _settings->inconclusive; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { const Token* toTok = nullptr; const Token* fromTok = nullptr; // Find cast if (Token::Match(tok, "( const| %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; std::string toStr = toType->isIntegral() ? "integer *" : toType->str(); toStr = toStr.substr(0, toStr.size()-2); std::string fromStr = fromType->isIntegral() ? "integer *" : fromType->str(); fromStr = fromStr.substr(0, fromStr.size() - 2); invalidPointerCastError(tok, fromStr, toStr, toType->type == ValueType::Type::CHAR); } } } } void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive) { if (to == "integer") { // If we cast something to int*, this can be useful to play with its binary data representation if (!inconclusive) reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to integer* is not portable due to different binary data representations on different platforms.", CWE704, false); else reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to char* is not portable due to different binary data representations on different platforms.", CWE704, true); } 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 (!_settings->standards.posix) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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", "Buffer '" + strVarName + "' 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 '" + strVarName + "' is an array of size " + strDim + ", which does not match.", CWE686, false); } //--------------------------------------------------------------------------- // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- static bool nonLocal(const Variable* var) { return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference(); } static bool nonLocalVolatile(const Variable* var) { if (var && var->isVolatile()) return false; return nonLocal(var); } static void eraseNotLocalArg(std::map& container, const SymbolDatabase* symbolDatabase) { for (std::map::iterator i = container.begin(); i != container.end();) { const Variable* var = symbolDatabase->getVariableFromVarId(i->first); if (!var || nonLocal(var)) { container.erase(i++); } else ++i; } } static void eraseMemberAssignments(const unsigned int varId, const std::map > &membervars, std::map &varAssignments) { const std::map >::const_iterator it = membervars.find(varId); if (it != membervars.end()) { const std::set& v = it->second; for (std::set::const_iterator vit = v.begin(); vit != v.end(); ++vit) { varAssignments.erase(*vit); if (*vit != varId) eraseMemberAssignments(*vit, membervars, varAssignments); } } } static bool checkExceptionHandling(const Token* tok) { const Variable* var = tok->variable(); const Scope* upperScope = tok->scope(); if (var && upperScope == var->scope()) return true; while (upperScope && upperScope->type != Scope::eTry && upperScope->type != Scope::eLambda && (!var || upperScope->nestedIn != var->scope()) && upperScope->isExecutable()) { upperScope = upperScope->nestedIn; } if (var && upperScope && upperScope->type == Scope::eTry) { // Check all exception han const Token* tok2 = upperScope->classEnd; while (Token::simpleMatch(tok2, "} catch (")) { tok2 = tok2->linkAt(2)->next(); if (Token::findmatch(tok2, "%varid%", tok2->link(), var->declarationId())) return false; tok2 = tok2->link(); } } return true; } void CheckOther::checkRedundantAssignment() { const bool printPerformance = _settings->isEnabled(Settings::PERFORMANCE); const bool printStyle = _settings->isEnabled(Settings::STYLE); const bool printWarning = _settings->isEnabled(Settings::WARNING); if (!printWarning && !printPerformance && !printStyle) return; const bool printInconclusive = _settings->inconclusive; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (!scope->isExecutable()) continue; std::map varAssignments; std::map memAssignments; std::map > membervars; std::set initialized; const Token* writtenArgumentsEnd = nullptr; for (const Token* tok = scope->classStart->next(); tok && tok != scope->classEnd; tok = tok->next()) { if (tok == writtenArgumentsEnd) writtenArgumentsEnd = nullptr; if (tok->str() == "?" && tok->astOperand2()) { tok = Token::findmatch(tok->astOperand2(), ";|}"); if (!tok) break; varAssignments.clear(); memAssignments.clear(); } else if (tok->str() == "{" && tok->strAt(-1) != "{" && tok->strAt(-1) != "=" && tok->strAt(-4) != "case" && tok->strAt(-3) != "default") { // conditional or non-executable inner scope: Skip it and reset status tok = tok->link(); varAssignments.clear(); memAssignments.clear(); } else if (Token::Match(tok, "for|if|while (")) { tok = tok->linkAt(1); } else if (Token::Match(tok, "break|return|continue|throw|goto|asm")) { varAssignments.clear(); memAssignments.clear(); } else if (tok->tokType() == Token::eVariable && !Token::Match(tok, "%name% (")) { const Token *eq = nullptr; for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[([]")) { // bail out if there is a variable in rhs - we only track 1 variable bool bailout = false; for (const Token *tok3 = tok2->link(); tok3 != tok2; tok3 = tok3->previous()) { if (tok3->varId()) { const Variable *var = tok3->variable(); if (!var || !var->isConst() || var->isReference() || var->isPointer()) { bailout = true; break; } } } if (bailout) break; tok2 = tok2->link(); } else if (Token::Match(tok2, "[)];,]")) break; else if (tok2->str() == "=") { eq = tok2; break; } } // Set initialization flag if (!Token::Match(tok, "%var% [")) initialized.insert(tok->varId()); else { const Token *tok2 = tok->next(); while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); if (tok2 && tok2->str() != ";") initialized.insert(tok->varId()); } const Token *startToken = tok; while (Token::Match(startToken, "%name%|::|.")) { startToken = startToken->previous(); if (Token::Match(startToken, "%name% . %var%")) membervars[startToken->varId()].insert(startToken->tokAt(2)->varId()); } std::map::iterator it = varAssignments.find(tok->varId()); if (eq && Token::Match(startToken, "[;{}]")) { // Assignment if (it != varAssignments.end()) { const Token *oldeq = nullptr; for (const Token *tok2 = it->second; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[([]")) tok2 = tok2->link(); else if (Token::Match(tok2, "[)];,]")) break; else if (Token::Match(tok2, "++|--|=")) { oldeq = tok2; break; } } if (!oldeq) { const Token *tok2 = it->second; while (Token::Match(tok2, "%name%|.|[|*|(")) tok2 = tok2->astParent(); if (Token::Match(tok2, "++|--")) oldeq = tok2; } // Ensure that LHS in assignments are the same bool error = oldeq && eq->astOperand1() && isSameExpression(_tokenizer->isCPP(), true, eq->astOperand1(), oldeq->astOperand1(), _settings->library, true); // Ensure that variable is not used on right side std::stack tokens; tokens.push(eq->astOperand2()); while (!tokens.empty()) { const Token *rhs = tokens.top(); tokens.pop(); if (!rhs) continue; tokens.push(rhs->astOperand1()); tokens.push(rhs->astOperand2()); if (rhs->varId() == tok->varId()) { error = false; break; } if (Token::Match(rhs->previous(), "%name% (") && nonLocalVolatile(tok->variable())) { // Called function might use the variable const Function* const func = rhs->function(); const Variable* const var = tok->variable(); if (!var || var->isGlobal() || var->isReference() || ((!func || func->nestedIn) && rhs->strAt(-1) != ".")) {// Global variable, or member function error = false; break; } } } if (error) { if (printWarning && scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok)) redundantAssignmentInSwitchError(it->second, tok, eq->astOperand1()->expressionString()); else if (printStyle) { // c++, unknown type => assignment might have additional side effects const bool possibleSideEffects(_tokenizer->isCPP() && !tok->valueType()); // TODO nonlocal variables are not tracked entirely. const bool nonlocal = it->second->variable() && nonLocalVolatile(it->second->variable()); // Warnings are inconclusive if there are possible side effects or if variable is not // tracked perfectly. const bool inconclusive = possibleSideEffects | nonlocal; if (printInconclusive || !inconclusive) if (_tokenizer->isC() || checkExceptionHandling(tok)) // see #6555 to see how exception handling might have an impact redundantAssignmentError(it->second, tok, eq->astOperand1()->expressionString(), inconclusive); } } it->second = tok; } if (!Token::simpleMatch(tok->tokAt(2), "0 ;") || (tok->variable() && tok->variable()->nameToken() != tok->tokAt(-2))) varAssignments[tok->varId()] = tok; memAssignments.erase(tok->varId()); eraseMemberAssignments(tok->varId(), membervars, varAssignments); } else if ((tok->next() && tok->next()->tokType() == Token::eIncDecOp) || (tok->previous()->tokType() == Token::eIncDecOp && tok->strAt(1) == ";")) { // Variable incremented/decremented; Prefix-Increment is only suspicious, if its return value is unused varAssignments[tok->varId()] = tok; memAssignments.erase(tok->varId()); eraseMemberAssignments(tok->varId(), membervars, varAssignments); } else if (!Token::simpleMatch(tok->tokAt(-2), "sizeof (")) { // Other usage of variable if (it != varAssignments.end()) varAssignments.erase(it); if (!writtenArgumentsEnd) // Indicates that we are in the first argument of strcpy/memcpy/... function memAssignments.erase(tok->varId()); } } else if (Token::Match(tok, "%name% (") && !_settings->library.isFunctionConst(tok->str(), true)) { // Function call. Global variables might be used. Reset their status const bool memfunc = Token::Match(tok, "memcpy|memmove|memset|strcpy|strncpy|sprintf|snprintf|strcat|strncat|wcscpy|wcsncpy|swprintf|wcscat|wcsncat"); if (tok->varId()) // operator() or function pointer varAssignments.erase(tok->varId()); if (memfunc && tok->strAt(-1) != "(" && tok->strAt(-1) != "=") { const Token* param1 = tok->tokAt(2); writtenArgumentsEnd = param1->next(); if (param1->varId() && param1->strAt(1) == "," && !Token::Match(tok, "strcat|strncat|wcscat|wcsncat") && param1->variable() && param1->variable()->isLocal() && param1->variable()->isArray()) { if (tok->str() == "memset" && initialized.find(param1->varId()) == initialized.end()) initialized.insert(param1->varId()); else { const std::map::const_iterator it = memAssignments.find(param1->varId()); if (it == memAssignments.end()) memAssignments[param1->varId()] = tok; else { bool read = false; for (const Token *tok2 = tok->linkAt(1); tok2 != writtenArgumentsEnd; tok2 = tok2->previous()) { if (tok2->varId() == param1->varId()) { // TODO: is this a read? maybe it's a write read = true; break; } } if (read) { memAssignments[param1->varId()] = tok; continue; } if (printWarning && scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok)) redundantCopyInSwitchError(it->second, tok, param1->str()); else if (printPerformance) redundantCopyError(it->second, tok, param1->str()); } } } } else if (scope->type == Scope::eSwitch) { // Avoid false positives if noreturn function is called in switch const Function* const func = tok->function(); if (!func || !func->hasBody()) { varAssignments.clear(); memAssignments.clear(); continue; } const Token* funcEnd = func->functionScope->classEnd; bool noreturn; if (!_tokenizer->IsScopeNoReturn(funcEnd, &noreturn) && !noreturn) { eraseNotLocalArg(varAssignments, symbolDatabase); eraseNotLocalArg(memAssignments, symbolDatabase); } else { varAssignments.clear(); memAssignments.clear(); } } else { // Noreturn functions outside switch don't cause problems eraseNotLocalArg(varAssignments, symbolDatabase); eraseNotLocalArg(memAssignments, symbolDatabase); } } } } } void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) { const std::list callstack = make_container< std::list >() << tok1 << tok2; reportError(callstack, Severity::performance, "redundantCopy", "Buffer '" + var + "' 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 = make_container< std::list >() << tok1 << tok2; reportError(callstack, Severity::warning, "redundantCopyInSwitch", "Buffer '" + var + "' 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 std::list callstack = make_container< std::list >() << tok1 << tok2; if (inconclusive) reportError(callstack, Severity::style, "redundantAssignment", "Variable '" + var + "' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" "Variable '" + var + "' 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(callstack, Severity::style, "redundantAssignment", "Variable '" + var + "' is reassigned a value before the old one has been used.", CWE563, false); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { const std::list callstack = make_container< std::list >() << tok1 << tok2; reportError(callstack, Severity::warning, "redundantAssignInSwitch", "Variable '" + var + "' 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Find the beginning of a switch. E.g.: // switch (var) { ... for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eSwitch || !i->classStart) continue; // Check the contents of the switch statement std::map varsWithBitsSet; std::map bitOperations; for (const Token *tok2 = i->classStart->next(); tok2 != i->classEnd; 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%")) { std::string bitOp = tok2->strAt(1)[0] + tok2->strAt(2); 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()) { std::string bitOp = tok2->strAt(3) + tok2->strAt(4); 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", "Redundant bitwise operation on '" + varname + "' in 'switch' statement. 'break;' missing?"); } //--------------------------------------------------------------------------- // Check for statements like case A||B: in switch() //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousCaseInSwitch() { if (!_settings->inconclusive || !_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eSwitch) continue; for (const Token* tok = i->classStart->next(); tok != i->classEnd; 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); } //--------------------------------------------------------------------------- // if (x == 1) // x == 0; // <- suspicious equality comparison. //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousEqualityComparison() { if (!_settings->isEnabled(Settings::WARNING) || !_settings->inconclusive) return; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "for (")) { const Token* const openParen = tok->next(); const Token* const closeParen = tok->linkAt(1); // Search for any suspicious equality comparison in the initialization // or increment-decrement parts of the for() loop. // For example: // for (i == 2; i < 10; i++) // or // for (i = 0; i < 10; i == a) if (Token::Match(openParen->next(), "%name% ==")) suspiciousEqualityComparisonError(openParen->tokAt(2)); if (closeParen->strAt(-2) == "==") suspiciousEqualityComparisonError(closeParen->tokAt(-2)); // Skip over for() loop conditions because "for (;running==1;)" // is a bit strange, but not necessarily incorrect. tok = closeParen; } else if (Token::Match(tok, "[;{}] *| %name% == %any% ;")) { // Exclude compound statements surrounded by parentheses, such as // printf("%i\n", ({x==0;})); // because they may appear as an expression in GNU C/C++. // See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html const Token* afterStatement = tok->strAt(1) == "*" ? tok->tokAt(6) : tok->tokAt(5); if (!Token::simpleMatch(afterStatement, "} )")) suspiciousEqualityComparisonError(tok->next()); } } } } void CheckOther::suspiciousEqualityComparisonError(const Token* tok) { reportError(tok, Severity::warning, "suspiciousEqualityComparison", "Found suspicious equality comparison. Did you intend to assign a value instead?", CWE482, 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 (!_settings->isEnabled(Settings::STYLE)) return; const bool printInconclusive = _settings->inconclusive; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { const Token* secondBreak = nullptr; const Token* labelName = nullptr; if (tok->link() && Token::Match(tok, "(|[|<")) tok = tok->link(); else if (Token::Match(tok, "break|continue ;")) secondBreak = tok->tokAt(2); else if (Token::Match(tok, "[;{}:] return|throw")) { tok = 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% (") && _settings->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->classEnd) 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 (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); if (!var || !var->isLocal() || (!var->isPointer() && !var->typeStartToken()->isStandardType())) continue; if (var->isConst()) 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()->classEnd; 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 (std::list::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if (used) { bool used2 = false; if (!checkInnerScope((*i)->classStart, var, used2) || used2) { return false; } } else if (!checkInnerScope((*i)->classStart, 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; unsigned 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", "The scope of the variable '" + varname + "' can be reduced.\n" "The scope of the variable '" + varname + "' 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 (!_settings->experimental) return; if (!_settings->isEnabled(Settings::STYLE)) return; for (const Token *tok = _tokenizer->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 reference //--------------------------------------------------------------------------- static std::size_t estimateSize(const Type* type, const Settings* settings, const SymbolDatabase* symbolDatabase, std::size_t recursionDepth = 0) { if (recursionDepth > 20) return 0; std::size_t cumulatedSize = 0; for (std::list::const_iterator i = type->classScope->varlist.cbegin(); i != type->classScope->varlist.cend(); ++i) { std::size_t size = 0; if (i->isStatic()) continue; if (i->isPointer() || i->isReference()) size = settings->sizeof_pointer; else if (i->type() && i->type()->classScope) size = estimateSize(i->type(), settings, symbolDatabase, recursionDepth+1); else if (i->isStlStringType() || (i->isStlType() && Token::Match(i->typeStartToken(), "std :: %type% <") && !Token::simpleMatch(i->typeStartToken()->linkAt(3), "> ::"))) size = 3 * settings->sizeof_pointer; // Just guess else size = symbolDatabase->sizeOfType(i->typeStartToken()); if (i->isArray()) cumulatedSize += size*i->dimension(0); else cumulatedSize += size; } for (std::vector::const_iterator i = type->derivedFrom.cbegin(); i != type->derivedFrom.cend(); ++i) if (i->type && i->type->classScope) cumulatedSize += estimateSize(i->type, settings, symbolDatabase, recursionDepth+1); return cumulatedSize; } void CheckOther::checkPassByReference() { if (!_settings->isEnabled(Settings::PERFORMANCE) || _tokenizer->isC()) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); 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) bool inconclusive = false; const Token* const tok = var->typeStartToken(); if (var->isStlStringType()) { ; } else if (var->isStlType() && Token::Match(tok, "std :: %type% <") && !Token::simpleMatch(tok->linkAt(3), "> ::") && !Token::Match(tok->tokAt(2), "initializer_list|weak_ptr|auto_ptr|unique_ptr")) { ; } 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(), _settings, symbolDatabase) <= 2 * _settings->sizeof_pointer) continue; } else continue; if (inconclusive && !_settings->inconclusive) continue; bool isConst = var->isConst(); if (!isConst) { // Check if variable could be const if (!var->scope() || var->scope()->function->isVirtual()) continue; isConst = true; for (const Token* tok2 = var->scope()->classStart; tok2 != var->scope()->classEnd; tok2 = tok2->next()) { if (tok2->varId() == var->declarationId()) { const Token* parent = tok2->astParent(); if (!parent) ; else if (parent->str() == "<<" || parent->str() == ">>") { if (parent->str() == "<<" && parent->astOperand1() == tok2) isConst = false; else if (parent->str() == ">>" && parent->astOperand2() == tok2) isConst = false; } else if (parent->str() == "," || parent->str() == "(") { // function argument const Token* tok3 = tok2->previous(); unsigned 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()) isConst = false; else { const Variable* argVar = tok3->astOperand1()->function()->getArgumentVar(argNr); if (!argVar|| (!argVar->isConst() && argVar->isReference())) isConst = false; } } else if (parent->isConstOp()) ; else if (parent->isAssignmentOp()) { if (parent->astOperand1() == tok2) isConst = false; else if (parent->astOperand1()->str() == "&") { const Variable* assignedVar = parent->previous()->variable(); if (!assignedVar || !assignedVar->isConst()) isConst = false; } } else if (Token::Match(tok2, "%var% . %name% (")) { const Function* func = tok2->tokAt(2)->function(); if (func && (func->isConst() || func->isStatic())) ; else isConst = false; } else isConst = false; if (!isConst) break; } } } if (isConst) passedByValueError(tok, var->name(), inconclusive); } } void CheckOther::passedByValueError(const Token *tok, const std::string &parname, bool inconclusive) { reportError(tok, Severity::performance, "passedByValue", "Function parameter '" + parname + "' should be passed by reference.\n" "Parameter '" + parname + "' is passed by value. It could be passed " "as a (const) reference which is usually faster and recommended in C++.", CWE398, inconclusive); } //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- void CheckOther::checkCharVariable() { const bool warning = _settings->isEnabled(Settings::WARNING); const bool portability = _settings->isEnabled(Settings::PORTABILITY); if (!warning && !portability) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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, _settings)) signedCharArrayIndexError(tok); if (portability && astIsUnknownSignChar(index) && index->getValueGE(0x80, _settings)) unknownSignCharArrayIndexError(tok); } else if (warning && Token::Match(tok, "[&|^]") && tok->astOperand2() && tok->astOperand1()) { bool warn = false; if (astIsSignedChar(tok->astOperand1())) { const ValueFlow::Value *v1 = tok->astOperand1()->getValueLE(-1, _settings); const ValueFlow::Value *v2 = tok->astOperand2()->getMaxValue(false); if (!v1) v1 = tok->astOperand1()->getValueGE(0x80, _settings); if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) warn = true; } else if (!warn && astIsSignedChar(tok->astOperand2())) { const ValueFlow::Value *v1 = tok->astOperand2()->getValueLE(-1, _settings); const ValueFlow::Value *v2 = tok->astOperand1()->getMaxValue(false); if (!v1) v1 = tok->astOperand2()->getValueGE(0x80, _settings); 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 that 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.. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteStatement() { if (!_settings->isEnabled(Settings::WARNING)) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "(|[")) tok = tok->link(); else if (tok->str() == "{" && tok->astParent()) tok = tok->link(); // C++11 struct/array/etc initialization in initializer list else if (Token::Match(tok->previous(), "%name%|] {") && !Token::findsimplematch(tok,";",tok->link())) tok = tok->link(); if (!Token::Match(tok, "[;{}] %str%|%num%")) continue; // No warning if numeric constant is followed by a "." or "," if (Token::Match(tok->next(), "%num% [,.]")) continue; // No warning for [;{}] (void *) 0 ; if (Token::Match(tok, "[;{}] 0 ;") && (tok->next()->isCast() || tok->next()->isExpandedMacro())) continue; // 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; break; } else if (tok2->str() == ";") break; } if (bailout) continue; // no warning if this is the last statement in a ({}) for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (Token::Match(tok2, "[;{}]")) { bailout = Token::simpleMatch(tok2, "; } )"); break; } } if (bailout) continue; constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string"); } } 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.", CWE398, false); } //--------------------------------------------------------------------------- // Detect division by zero. //--------------------------------------------------------------------------- void CheckOther::checkZeroDivision() { for (const Token *tok = _tokenizer->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; if (tok->astOperand1()->isNumber()) { if (MathLib::isFloat(tok->astOperand1()->str())) continue; } else if (tok->astOperand1()->isName()) { if (!tok->astOperand1()->valueType()->isIntegral()) continue; } else if (!tok->astOperand1()->isArithmeticalOp()) continue; // Value flow.. const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); if (value && _settings->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) { unsigned 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() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "inf.0 +|-") || Token::Match(tok, "+|- inf.0") || Token::Match(tok, "+|- %num% / 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 (_tokenizer->isC()) return; if (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; 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", "Instance of '" + varname + "' object is destroyed immediately.", CWE563, false); } //----------------------------------------------------------------------------- // 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 (!_settings->isEnabled(Settings::STYLE) || !_settings->inconclusive) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (scope->type != Scope::eIf) continue; // check all the code in the function for if (..) else if (Token::simpleMatch(scope->classEnd, "} else {")) { // Make sure there are no macros (different macros might be expanded // to the same code) bool macro = false; for (const Token *tok = scope->classStart; tok != scope->classEnd->linkAt(2); tok = tok->next()) { if (tok->isExpandedMacro()) { macro = true; break; } } if (macro) continue; // save if branch code std::string branch1 = scope->classStart->next()->stringifyList(scope->classEnd); if (branch1.empty()) continue; // save else branch code std::string branch2 = scope->classEnd->tokAt(3)->stringifyList(scope->classEnd->linkAt(2)); // check for duplicates if (branch1 == branch2) duplicateBranchError(scope->classDef, scope->classEnd->next()); } } } void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2) { const std::list toks = make_container< std::list >() << tok2 << tok1; reportError(toks, 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 allocatedVariables; const bool printInconclusive = _settings->inconclusive; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { // Keep track of which variables were assigned addresses to newly-allocated memory if (Token::Match(tok, "%var% = malloc|g_malloc|new")) { allocatedVariables.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() && allocatedVariables.find(tok->varId()) != allocatedVariables.end()) { if (printInconclusive) allocatedVariables[tok->varId()] = true; else allocatedVariables.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% =")) { allocatedVariables.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 unsigned int var1 = tok->tokAt(varIndex)->varId(); const unsigned int var2 = tok->tokAt(varIndex + 2)->varId(); const std::map::const_iterator alloc1 = allocatedVariables.find(var1); const std::map::const_iterator alloc2 = allocatedVariables.find(var2); if (alloc1 != allocatedVariables.end()) { invalidFreeError(tok, alloc1->second); } else if (alloc2 != allocatedVariables.end()) { invalidFreeError(tok, 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% (") && !_settings->library.isFunctionConst(tok->str(), true)) { const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1)); while (tok2 != nullptr) { allocatedVariables.erase(tok2->varId()); tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1)); } } } } } void CheckOther::invalidFreeError(const Token *tok, bool inconclusive) { reportError(tok, Severity::error, "invalidFree", "Invalid memory address freed.", 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) { std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { std::list::const_iterator func; // 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 (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { functionsByName[func->tokenDef->str()].push_back(&*func); } for (StringFunctionMap::iterator it = functionsByName.begin(); it != functionsByName.end(); ++it) { 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=_settings->isEnabled(Settings::STYLE); const bool warningEnabled=_settings->isEnabled(Settings::WARNING); if (!styleEnabled && !warningEnabled) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; std::list constFunctions; getConstFunctions(symbolDatabase, constFunctions); 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->classEnd; tok = tok->next()) { if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=")) { if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true)) continue; if (isSameExpression(_tokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), _settings->library, true)) { if (isWithoutSideEffects(_tokenizer->isCPP(), tok->astOperand1())) { const bool assignment = tok->str() == "="; if (assignment && warningEnabled) selfAssignmentError(tok, tok->astOperand1()->expressionString()); else if (styleEnabled) { if (_tokenizer->isCPP() && _settings->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, tok, tok->str()); } } } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(_tokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), _settings->library, true) && isWithoutSideEffects(_tokenizer->isCPP(), tok->astOperand2())) duplicateExpressionError(tok->astOperand2(), tok->astOperand2(), tok->str()); else if (tok->astOperand2()) { const Token *ast1 = tok->astOperand1(); while (ast1 && tok->str() == ast1->str()) { if (isSameExpression(_tokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), _settings->library, true) && isWithoutSideEffects(_tokenizer->isCPP(), ast1->astOperand1())) // TODO: warn if variables are unchanged. See #5683 // Probably the message should be changed to 'duplicate expressions X in condition or something like that'. ;//duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok->str()); else if (styleEnabled && isSameExpression(_tokenizer->isCPP(), true, ast1->astOperand2(), tok->astOperand2(), _settings->library, true) && isWithoutSideEffects(_tokenizer->isCPP(), ast1->astOperand2())) duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok->str()); if (!isConstExpression(ast1->astOperand2(), _settings->library, true)) break; ast1 = ast1->astOperand1(); } } } } else if (styleEnabled && tok->astOperand1() && tok->astOperand2() && tok->str() == ":" && tok->astParent() && tok->astParent()->str() == "?") { if (isSameExpression(_tokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), _settings->library, false)) duplicateExpressionTernaryError(tok); } } } } void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op) { const std::list toks = make_container< std::list >() << tok2 << 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.", CWE398, false); } void CheckOther::duplicateExpressionTernaryError(const Token *tok) { reportError(tok, 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::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "Redundant assignment of '" + varname + "' 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% )")) { const unsigned int varidLeft = tok->tokAt(2)->varId();// get the left varid const unsigned 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", "Comparison of two identical variables with " + functionName + "(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n" "The function " + functionName + " 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 (!_settings->isEnabled(Settings::STYLE)) return; const bool inconclusive = _tokenizer->codeWithTemplates(); if (inconclusive && !_settings->inconclusive) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // check all the code in the function for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) continue; if (Token::Match(tok, "<|<= 0") && tok->next() == tok->astOperand2()) { const ValueType* vt = tok->astOperand1()->valueType(); if (vt && vt->pointer) pointerLessThanZeroError(tok, inconclusive); if (vt && vt->sign == ValueType::UNSIGNED) unsignedLessThanZeroError(tok, tok->astOperand1()->expressionString(), inconclusive); } else if (Token::Match(tok->previous(), "0 >|>=") && tok->previous() == tok->astOperand1()) { const ValueType* vt = tok->astOperand2()->valueType(); if (vt && vt->pointer) pointerLessThanZeroError(tok, inconclusive); if (vt && vt->sign == ValueType::UNSIGNED) unsignedLessThanZeroError(tok, tok->astOperand2()->expressionString(), inconclusive); } else if (Token::simpleMatch(tok, ">= 0") && tok->next() == tok->astOperand2()) { const ValueType* vt = tok->astOperand1()->valueType(); if (vt && vt->pointer) pointerPositiveError(tok, inconclusive); if (vt && vt->sign == ValueType::UNSIGNED) unsignedPositiveError(tok, tok->astOperand1()->str(), inconclusive); } else if (Token::simpleMatch(tok->previous(), "0 <=") && tok->previous() == tok->astOperand1()) { const ValueType* vt = tok->astOperand2()->valueType(); if (vt && vt->pointer) pointerPositiveError(tok, inconclusive); if (vt && vt->sign == ValueType::UNSIGNED) unsignedPositiveError(tok, tok->astOperand2()->str(), inconclusive); } } } } void CheckOther::unsignedLessThanZeroError(const Token *tok, const std::string &varname, bool inconclusive) { if (inconclusive) { reportError(tok, Severity::style, "unsignedLessThanZero", "Checking if unsigned variable '" + varname + "' is less than zero. This might be a false warning.\n" "Checking if unsigned variable '" + varname + "' is less than zero. An unsigned " "variable will never be negative so it is either pointless or an error to check if it is. " "It's not known if the used constant is a template parameter or not and therefore " "this message might be a false warning.", CWE570, true); } else { reportError(tok, Severity::style, "unsignedLessThanZero", "Checking if unsigned variable '" + varname + "' is less than zero.\n" "The unsigned variable '" + varname + "' 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, bool inconclusive) { reportError(tok, Severity::style, "pointerLessThanZero", "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, inconclusive); } void CheckOther::unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive) { if (inconclusive) { reportError(tok, Severity::style, "unsignedPositive", "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.\n" "The unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it. " "It's not known if the used constant is a " "template parameter or not and therefore this message might be a false warning", CWE570, true); } else { reportError(tok, Severity::style, "unsignedPositive", "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.", CWE570, false); } } void CheckOther::pointerPositiveError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "pointerPositive", "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, inconclusive); } /* check if a constructor in given class scope takes a reference */ static bool constructorTakesReference(const Scope * const classScope) { for (std::list::const_iterator func = classScope->functionList.begin(); func != classScope->functionList.end(); ++func) { if (func->isConstructor()) { const Function &constructor = *func; for (std::size_t 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 (!_settings->isEnabled(Settings::PERFORMANCE) || _tokenizer->isC() || !_settings->inconclusive) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::size_t i = 1; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); 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", "Use const reference for '" + varname + "' to avoid unnecessary data copying.\n" "The const variable '"+varname+"' is assigned a copy of the data. You can avoid " "the unnecessary data copying by converting '" + varname + "' 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 = _settings->isEnabled(Settings::PORTABILITY); for (const Token* tok = _tokenizer->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 (_tokenizer->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(), _settings)) negativeBitwiseShiftError(tok, 1); else if (isNegative(tok->astOperand2(), _settings)) 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 (!_settings->inconclusive) return; const bool printWarning = _settings->isEnabled(Settings::WARNING); const bool printPortability = _settings->isEnabled(Settings::PORTABILITY); if (!printPortability && !printWarning) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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)) { unsigned int size = _tokenizer->sizeOfType(var->typeStartToken()); if (size == 0 && var->typeStartToken()->next()->str() == "*") size = _settings->sizeof_pointer; if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { if (printWarning) incompleteArrayFillError(tok, var->name(), tok->str(), false); } else if (Token::Match(var->typeStartToken(), "bool|_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", "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", "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 (!_settings->isEnabled(Settings::PORTABILITY)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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; std::size_t 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(-3), ". . .")) 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; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "&") { // bail out for logical AND operator if (tok->astOperand2()) continue; // pointer dereference const Token *astTok = tok->astOperand1(); if (!astTok || astTok->str() != "*") continue; // variable const Token *varTok = astTok->astOperand1(); if (!varTok || varTok->isExpandedMacro() || varTok->varId() == 0) continue; const Variable *var = symbolDatabase->getVariableFromVarId(varTok->varId()); 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", "Redundant pointer operation on '" + varname + "' - it's already a pointer.", CWE398, inconclusive); } void CheckOther::checkInterlockedDecrement() { if (!_settings->isWindowsPlatform()) { return; } for (const Token *tok = _tokenizer->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 (!_settings->isEnabled(Settings::STYLE) && !_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (!tok->scope()->isExecutable()) tok = tok->scope()->classEnd; if (Token::Match(tok, "{|}|; %name% :") && tok->strAt(1) != "default") { if (!Token::findsimplematch(scope->classStart->next(), ("goto " + tok->strAt(1)).c_str(), scope->classEnd->previous())) unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch); } } } } void CheckOther::unusedLabelError(const Token* tok, bool inSwitch) { if (inSwitch) { if (!tok || _settings->isEnabled(Settings::WARNING)) reportError(tok, Severity::warning, "unusedLabelSwitch", "Label '" + (tok ? tok->str() : emptyString) + "' is not used. Should this be a 'case' of the enclosing switch()?", CWE398, false); } else { if (!tok || _settings->isEnabled(Settings::STYLE)) reportError(tok, Severity::style, "unusedLabel", "Label '" + (tok ? tok->str() : emptyString) + "' is not used.", CWE398, false); } } void CheckOther::checkEvaluationOrder() { // This checker is not written according to C++11 sequencing rules if (_tokenizer->isCPP() && _settings->standards.cpp >= Standards::CPP11) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * functionScope = symbolDatabase->functionScopes[i]; for (const Token* tok = functionScope->classStart; tok != functionScope->classEnd; 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 => break if (!(par && par->str() == "(" && par->astOperand2())) 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(_tokenizer->isCPP(), false, tok->astOperand1(), parent->astOperand1(), _settings->library, true)) { if (_settings->isEnabled(Settings::WARNING) && isSameExpression(_tokenizer->isCPP(), true, tok->astOperand1(), parent->astOperand1(), _settings->library, true)) selfAssignmentError(parent, tok->astOperand1()->expressionString()); break; } // Is expression used? bool foundError = false; std::stack tokens; tokens.push((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2()); while (!tokens.empty() && !foundError) { const Token * const tok3 = tokens.top(); tokens.pop(); if (!tok3) continue; if (tok3->str() == "&" && !tok3->astOperand2()) continue; // don't handle address-of for now if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) continue; // don't care about sizeof usage tokens.push(tok3->astOperand1()); tokens.push(tok3->astOperand2()); if (isSameExpression(_tokenizer->isCPP(), false, tok->astOperand1(), tok3, _settings->library, true)) { foundError = true; } } 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 (!_tokenizer->isCPP() || _settings->standards.cpp < Standards::CPP11 || !_settings->isEnabled(Settings::WARNING)) return; const bool reportInconclusive = _settings->inconclusive; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; const Token * scopeStart = scope->classStart; if (scope->function) { const Token * memberInitializationStart = scope->function->constructorMemberInitialization(); if (memberInitializationStart) scopeStart = memberInitializationStart; } for (const Token* tok = scopeStart->next(); tok != scope->classEnd; tok = tok->next()) { const ValueFlow::Value * movedValue = tok->getMovedValue(); if (!movedValue || movedValue->moveKind == ValueFlow::Value::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 { bool isVariableChanged = isVariableChangedByFunctionCall(tok, _settings, &inconclusive); accessOfMoved = !isVariableChanged; 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::MovedVariable: errorId = "accessMoved"; kindString = "moved"; break; case ValueFlow::Value::ForwardedVariable: errorId = "accessForwarded"; kindString = "forwarded"; break; default: return; } const std::string errmsg("Access of " + kindString + " variable '" + varname + "'."); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive); } void CheckOther::checkFuncArgNamesDifferent() { const bool style = _settings->isEnabled(Settings::STYLE); const bool inconclusive = _settings->inconclusive; const bool warning = _settings->isEnabled(Settings::WARNING); if (!(warning || (style && inconclusive))) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // check every function for (std::size_t i = 0, end = symbolDatabase->functionScopes.size(); i < end; ++i) { const Function * function = symbolDatabase->functionScopes[i]->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 (std::size_t 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 (std::size_t j = 0; j < function->argCount(); ++j) { if (!declarations[j] || !definitions[j] || declarations[j]->str() == definitions[j]->str()) continue; for (std::size_t 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 (std::size_t 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, size_t index, const Token* declaration, const Token* definition) { std::list tokens; tokens.push_back(declaration); tokens.push_back(definition); reportError(tokens, Severity::style, "funcArgNamesDifferent", "Function '" + functionName + "' 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; tokens.push_back(declarations.size() ? declarations[0] ? declarations[0] : declaration : nullptr); tokens.push_back(definitions.size() ? definitions[0] ? definitions[0] : definition : nullptr); std::string msg = "Function '" + functionName + "' argument order different: declaration '"; for (std::size_t i = 0; i < declarations.size(); ++i) { if (i != 0) msg += ", "; if (declarations[i]) msg += declarations[i]->str(); } msg += "' definition '"; for (std::size_t 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); } cppcheck-1.82/lib/checkother.h000066400000000000000000000432751322667425100163050ustar00rootroot00000000000000/* * 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 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) { 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(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckOther checkOther(tokenizer, settings, errorLogger); // Checks checkOther.clarifyCalculation(); checkOther.clarifyStatement(); checkOther.checkPassByReference(); checkOther.checkIncompleteStatement(); checkOther.checkCastIntToCharAndBack(); checkOther.checkMisusedScopedObject(); checkOther.checkPipeParameterSize(); checkOther.checkInvalidFree(); checkOther.checkRedundantCopy(); checkOther.checkSuspiciousEqualityComparison(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); 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(); /** @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 code like 'case A||B:'*/ void checkSuspiciousEqualityComparison(); /** @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, 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(); 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); void passedByValueError(const Token *tok, const std::string &parname, bool inconclusive); void constStatementError(const Token *tok, const std::string &type); 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 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 suspiciousEqualityComparisonError(const Token* tok); 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); void duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op); void duplicateExpressionTernaryError(const Token *tok); void duplicateBreakError(const Token *tok, bool inconclusive); void unreachableCodeError(const Token* tok, bool inconclusive); void unsignedLessThanZeroError(const Token *tok, const std::string &varname, bool inconclusive); void pointerLessThanZeroError(const Token *tok, bool inconclusive); void unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive); void pointerPositiveError(const Token *tok, bool inconclusive); 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, size_t 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckOther c(nullptr, settings, errorLogger); // error c.zerodivError(nullptr, nullptr); c.misusedScopeObjectError(nullptr, "varname"); c.invalidPointerCastError(nullptr, "float", "double", false); c.negativeBitwiseShiftError(nullptr, 1); c.negativeBitwiseShiftError(nullptr, 2); c.checkPipeParameterSizeError(nullptr, "varname", "dimension"); c.raceAfterInterlockedDecrementError(nullptr); //performance c.redundantCopyError(nullptr, "varname"); c.redundantCopyError(nullptr, nullptr, "var"); c.redundantAssignmentError(nullptr, nullptr, "var", false); // style/warning c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); c.passedByValueError(nullptr, "parametername", false); c.constStatementError(nullptr, "type"); 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.suspiciousEqualityComparisonError(nullptr); c.selfAssignmentError(nullptr, "varname"); c.clarifyCalculationError(nullptr, "+"); c.clarifyStatementError(nullptr); c.duplicateBranchError(nullptr, nullptr); c.duplicateExpressionError(nullptr, nullptr, "&&"); c.duplicateExpressionTernaryError(nullptr); c.duplicateBreakError(nullptr, false); c.unreachableCodeError(nullptr, false); c.unsignedLessThanZeroError(nullptr, "varname", false); c.unsignedPositiveError(nullptr, "varname", false); c.pointerLessThanZeroError(nullptr, false); c.pointerPositiveError(nullptr, false); 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); std::vector nullvec; c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); } static std::string myName() { return "Other"; } std::string classInfo() const { 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"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkotherH cppcheck-1.82/lib/checkpostfixoperator.cpp000066400000000000000000000066031322667425100207610ustar00rootroot00000000000000/* * 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 . */ //--------------------------------------------------------------------------- // 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 (!_settings->isEnabled(Settings::PERFORMANCE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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.82/lib/checkpostfixoperator.h000066400000000000000000000050741322667425100204270ustar00rootroot00000000000000/* * 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 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 runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { 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 { CheckPostfixOperator c(nullptr, settings, errorLogger); c.postfixOperatorError(nullptr); } static std::string myName() { return "Using postfix operators"; } std::string classInfo() const { return "Warn if using postfix operators ++ or -- rather than prefix operator\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkpostfixoperatorH cppcheck-1.82/lib/checksizeof.cpp000066400000000000000000000452241322667425100170120ustar00rootroot00000000000000/* * 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 "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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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 (!_settings->isEnabled(Settings::WARNING)) return; for (const Token *tok = _tokenizer->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 (!_settings->isEnabled(Settings::WARNING)) return; const bool printInconclusive = _settings->inconclusive; for (const Token *tok = _tokenizer->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; } } const Token *argument = tok->next()->astOperand2(); if (argument && argument->isCalculation() && (!argument->isExpandedMacro() || printInconclusive)) sizeofCalculationError(argument, argument->isExpandedMacro()); } } } void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive); } //----------------------------------------------------------------------------- // Check for code like sizeof()*sizeof() or sizeof(ptr)/value //----------------------------------------------------------------------------- void CheckSizeof::suspiciousSizeofCalculation() { if (!_settings->isEnabled(Settings::WARNING) || !_settings->inconclusive) return; // TODO: Use AST here. This should be possible as soon as sizeof without brackets is correctly parsed for (const Token *tok = _tokenizer->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 (!_settings->isEnabled(Settings::PORTABILITY)) return; for (const Token *tok = _tokenizer->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; bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); 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; bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); 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 = "'" + varname + "' 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", message + "\n" + verbose, CWE467, false); } cppcheck-1.82/lib/checksizeof.h000066400000000000000000000121301322667425100164450ustar00rootroot00000000000000/* * 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 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) { CheckSizeof checkSizeof(tokenizer, settings, errorLogger); // Checks checkSizeof.sizeofsizeof(); checkSizeof.sizeofCalculation(); checkSizeof.suspiciousSizeofCalculation(); checkSizeof.checkSizeofForArrayParameter(); checkSizeof.checkSizeofForPointerSize(); checkSizeof.checkSizeofForNumericParameter(); checkSizeof.sizeofVoid(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer* /*tokenizer*/, const Settings* /*settings*/, ErrorLogger* /*errorLogger*/) { } /** @brief %Check for 'sizeof sizeof ..' */ void sizeofsizeof(); /** @brief %Check for calculations inside sizeof */ void sizeofCalculation(); /** @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 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 { 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.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 { 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 suspicious calculations with sizeof()\n" "- using 'sizeof(void)' which is undefined\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checksizeofH cppcheck-1.82/lib/checkstl.cpp000066400000000000000000002361071322667425100163170ustar00rootroot00000000000000/* * 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 "checkstl.h" #include "checknullpointer.h" #include "errorlogger.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "utils.h" #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 // Error message for bad iterator usage.. void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) { reportError(tok, Severity::error, "invalidIterator1", "Invalid iterator: " + iteratorName, CWE664, false); } void CheckStl::iteratorsError(const Token *tok, const std::string &container1, const std::string &container2) { reportError(tok, Severity::error, "iterators", "Same iterator is used with different containers '" + container1 + "' and '" + container2 + "'.", 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; callstack.push_back(deref); callstack.push_back(erased); reportError(callstack, Severity::error, "eraseDereference", "Iterator '" + itername + "' used after element has been erased.\n" "The iterator '" + itername + "' 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", "Invalid iterator '" + itername + "' used.\n" "The iterator '" + itername + "' 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; } void CheckStl::iterators() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int iteratorId = 1; iteratorId < symbolDatabase->getVariableListSize(); iteratorId++) { const Variable* var = symbolDatabase->getVariableFromVarId(iteratorId); bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) continue; // 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()->classEnd; tok2 = tok2->next()) { if (invalidationScope && tok2 == invalidationScope->classEnd) validIterator = true; // Assume that the iterator becomes valid again if (containerAssignScope && tok2 == containerAssignScope->classEnd) containerToken = nullptr; // We don't know which containers might be used with the iterator if (tok2 == validatingToken) { validIterator = true; eraseToken = nullptr; invalidationScope = nullptr; } // Is iterator compared against different container? if (tok2->isComparisonOp() && containerToken && tok2->astOperand1() && tok2->astOperand2()) { const Token *other = nullptr; if (tok2->astOperand1()->varId() == iteratorId) other = tok2->astOperand2()->tokAt(-3); else if (tok2->astOperand2()->varId() == iteratorId) other = tok2->astOperand1()->tokAt(-3); if (Token::Match(other, "%name% . end|rend|cend|crend ( )") && other->varId() != containerToken->varId()) iteratorsError(tok2, getContainerName(containerToken), getContainerName(other)); } // Is the iterator used in a insert/erase operation? else if (Token::Match(tok2, "%name% . insert|erase ( *| %varid% )|,", iteratorId)) { 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; } // 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)) { // Assume that the iterator becomes valid. // TODO: add checking that checks if the iterator becomes valid or not validatingToken = Token::findmatch(tok2->tokAt(2), "[;)]"); // skip ahead tok2 = tok2->tokAt(2); } // 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; } } } } // 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); } static const std::set algorithm2 = make_container< std::set >() // 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 = make_container< std::set >() // 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 = make_container< std::set >() // 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; } void CheckStl::mismatchingContainers() { // Check if different containers are used in various calls of standard functions const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t ii = 0; ii < functions; ++ii) { const Scope * scope = symbolDatabase->functionScopes[ii]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% ( !!)")) continue; const Token * const ftok = tok; const Token * const arg1 = tok->tokAt(2); int argnr = 1; std::map containerNr; for (const Token *argTok = arg1; argTok; argTok = argTok->nextArgument()) { const Library::ArgumentChecks::IteratorInfo *i = _settings->library.getArgIteratorInfo(ftok,argnr++); if (!i) continue; const Variable *c = getContainer(argTok); if (!c) continue; 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); } } int ret = _settings->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) { std::map::const_iterator it = containerNr.find(c); if (it == containerNr.end() || it->second != ret) mismatchingContainersError(other); } } } } for (unsigned int varid = 0; varid < symbolDatabase->getVariableListSize(); varid++) { const Variable* var = symbolDatabase->getVariableFromVarId(varid); 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()); } } } } void CheckStl::stlOutOfBounds() { const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); // Scan through all scopes.. for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { const Token* tok = i->classDef; // only interested in conditions if ((i->type != Scope::eFor && i->type != Scope::eWhile && i->type != Scope::eIf && i->type != Scope::eDo) || !tok) continue; if (i->type == Scope::eFor) tok = Token::findsimplematch(tok->tokAt(2), ";"); else if (i->type == Scope::eDo) { tok = tok->linkAt(1)->tokAt(2); } else tok = tok->next(); if (!tok) continue; tok = tok->next(); // check if the for loop condition is wrong if (!Token::Match(tok, "%var% <= %var% . %name% ( ) ;|)|%oror%")) continue; // Is it a vector? const Variable *var = tok->tokAt(2)->variable(); if (!var) continue; const Library::Container* container = _settings->library.detectContainer(var->typeStartToken()); if (!container) continue; if (container->getYield(tok->strAt(4)) != Library::Container::SIZE) continue; // variable id for loop variable. const unsigned int numId = tok->varId(); // variable id for the container variable const unsigned int declarationId = var->declarationId(); for (const Token *tok3 = i->classStart; tok3 && tok3 != i->classEnd; tok3 = tok3->next()) { if (tok3->varId() == declarationId) { tok3 = tok3->next(); if (Token::Match(tok3, ". %name% ( )")) { if (container->getYield(tok3->strAt(1)) == Library::Container::SIZE) break; } else if (container->arrayLike_indexOp && Token::Match(tok3, "[ %varid% ]", numId)) stlOutOfBoundsError(tok3, tok3->strAt(1), var->name(), false); else if (Token::Match(tok3, ". %name% ( %varid% )", numId)) { Library::Container::Yield yield = container->getYield(tok3->strAt(1)); if (yield == Library::Container::AT_INDEX) stlOutOfBoundsError(tok3, tok3->strAt(3), var->name(), true); } } } } } void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at) { if (at) reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + ".at(" + num + ") is out of bounds.", CWE788, false); else reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + "[" + num + "] is out of bounds.", CWE788, false); } void CheckStl::negativeIndex() { // Negative index is out of bounds.. const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t ii = 0; ii < functions; ++ii) { const Scope * scope = symbolDatabase->functionScopes[ii]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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 = _settings->library.detectContainer(var->typeStartToken()); if (!container || !container->arrayLike_indexOp) continue; const ValueFlow::Value *index = tok->next()->astOperand2()->getValueLE(-1, _settings); 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 = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type == Scope::eFor && Token::simpleMatch(i->classDef, "for (")) { const Token *tok = i->classDef->linkAt(1); if (!Token::Match(tok->tokAt(-3), "; ++| %var% ++| ) {")) continue; tok = tok->previous(); if (!tok->isName()) tok = tok->previous(); eraseCheckLoopVar(*i, tok->variable()); } else if (i->type == Scope::eWhile && Token::Match(i->classDef, "while ( %var% !=")) { eraseCheckLoopVar(*i, i->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.classStart; tok != scope.classEnd; tok = tok->next()) { if (tok->str() != "(") continue; if (!Token::Match(tok->tokAt(-2), ". erase ( ++| %varid% )", var->declarationId())) continue; if (Token::simpleMatch(tok->astParent(), "=")) continue; // Iterator is invalid.. unsigned int indentlevel = 0U; const Token *tok2 = tok->link(); for (; tok2 != scope.classEnd; 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.classEnd) dereferenceErasedError(tok, scope.classDef, var->nameToken()->str(), inconclusiveType); } } void CheckStl::pushback() { // Pointer can become invalid after push_back, push_front, reserve or resize.. const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%var% = & %var% [")) { // Skip it directly if it is a pointer or an array const Token* containerTok = tok->tokAt(3); if (containerTok->variable() && containerTok->variable()->isArrayOrPointer()) continue; // Variable id for pointer const unsigned int pointerId(tok->varId()); bool invalidPointer = false; const Token* function = nullptr; const Token* end2 = tok->scope()->classEnd; for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { // push_back on vector.. if (Token::Match(tok2, "%varid% . push_front|push_back|insert|reserve|resize|clear", containerTok->varId())) { invalidPointer = true; function = tok2->tokAt(2); } // Using invalid pointer.. if (invalidPointer && tok2->varId() == pointerId) { bool unknown = false; if (CheckNullPointer::isPointerDeRef(tok2, unknown)) invalidPointerError(tok2, function->str(), tok2->str()); break; } } } } } // Iterator becomes invalid after reserve, resize, insert, push_back or push_front.. for (unsigned int iteratorId = 1; iteratorId < symbolDatabase->getVariableListSize(); iteratorId++) { const Variable* var = symbolDatabase->getVariableFromVarId(iteratorId); // Check that its an iterator if (!var || !var->isLocal() || !Token::Match(var->typeEndToken(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator")) continue; // ... on std::vector if (!Token::Match(var->typeStartToken(), "std| ::| vector <")) continue; // the variable id for the vector unsigned int vectorid = 0; const Token* validatingToken = nullptr; std::string invalidIterator; const Token* end2 = var->scope()->classEnd; for (const Token *tok2 = var->nameToken(); tok2 != end2; tok2 = tok2->next()) { if (validatingToken == tok2) { invalidIterator.clear(); validatingToken = nullptr; } // Using push_back or push_front inside a loop.. if (Token::simpleMatch(tok2, "for (")) { tok2 = tok2->tokAt(2); } if (Token::Match(tok2, "%varid% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %varid% != %var% . end|rend|cend|crend ( ) ; ++| %varid% ++| ) {", iteratorId)) { // variable id for the loop iterator const unsigned int varId(tok2->tokAt(2)->varId()); const Token *pushbackTok = nullptr; // Count { and } for tok3 const Token *tok3 = tok2->tokAt(20); for (const Token* const end3 = tok3->linkAt(-1); tok3 != end3; tok3 = tok3->next()) { if (tok3->str() == "break" || tok3->str() == "return") { pushbackTok = nullptr; break; } else if (Token::Match(tok3, "%varid% . push_front|push_back|insert|reserve|resize|clear|erase (", varId) && !tok3->previous()->isAssignmentOp()) { if (tok3->strAt(2) != "erase" || (tok3->tokAt(4)->varId() != iteratorId && tok3->tokAt(5)->varId() != iteratorId)) // This case is handled in: CheckStl::iterators() pushbackTok = tok3->tokAt(2); } } if (pushbackTok) invalidIteratorError(pushbackTok, pushbackTok->str(), tok2->str()); } // Assigning iterator.. if (Token::Match(tok2, "%varid% =", iteratorId)) { if (Token::Match(tok2->tokAt(2), "%var% . begin|end|rbegin|rend|cbegin|cend|crbegin|crend|insert|erase|find (")) { if (!invalidIterator.empty() && Token::Match(tok2->tokAt(4), "insert|erase ( *| %varid% )|,", iteratorId)) { invalidIteratorError(tok2, invalidIterator, var->name()); break; } vectorid = tok2->tokAt(2)->varId(); tok2 = tok2->linkAt(5); } else { vectorid = 0; } invalidIterator.clear(); } // push_back on vector.. if (vectorid > 0 && Token::Match(tok2, "%varid% . push_front|push_back|insert|reserve|resize|clear|erase (", vectorid)) { if (!invalidIterator.empty() && Token::Match(tok2->tokAt(2), "insert|erase ( *| %varid% ,|)", iteratorId)) { invalidIteratorError(tok2, invalidIterator, var->name()); break; } if (tok2->strAt(2) != "erase" || (tok2->tokAt(4)->varId() != iteratorId && tok2->tokAt(5)->varId() != iteratorId)) // This case is handled in: CheckStl::iterators() invalidIterator = tok2->strAt(2); tok2 = tok2->linkAt(3); } else if (tok2->str() == "return" || tok2->str() == "throw") validatingToken = Token::findsimplematch(tok2->next(), ";"); // TODO: instead of bail out for 'else' try to check all execution paths. else if (tok2->str() == "break" || tok2->str() == "else") invalidIterator.clear(); // Using invalid iterator.. if (!invalidIterator.empty()) { if (Token::Match(tok2, "++|--|*|+|-|(|,|=|!= %varid%", iteratorId)) invalidIteratorError(tok2, invalidIterator, tok2->strAt(1)); if (Token::Match(tok2, "%varid% ++|--|+|-|.", iteratorId)) invalidIteratorError(tok2, invalidIterator, tok2->str()); } } } } // Error message for bad iterator usage.. void CheckStl::invalidIteratorError(const Token *tok, const std::string &func, const std::string &iterator_name) { reportError(tok, Severity::error, "invalidIterator2", "After " + func + "(), the iterator '" + iterator_name + "' may be invalid.", CWE664, false); } // Error message for bad iterator usage.. void CheckStl::invalidPointerError(const Token *tok, const std::string &func, const std::string &pointer_name) { reportError(tok, Severity::error, "invalidPointer", "Invalid pointer '" + pointer_name + "' after " + func + "().", CWE664, false); } void CheckStl::stlBoundaries() { const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int iteratorId = 1; iteratorId < symbolDatabase->getVariableListSize(); iteratorId++) { const Variable* var = symbolDatabase->getVariableFromVarId(iteratorId); if (!var || !var->scope() || !var->scope()->isExecutable()) continue; const Library::Container* container = _settings->library.detectContainer(var->typeStartToken(), true); if (!container || container->opLessAllowed) continue; const Token* const end = var->scope()->classEnd; 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 = _settings->isEnabled(Settings::WARNING); const bool printPerformance = _settings->isEnabled(Settings::PERFORMANCE); if (!printWarning && !printPerformance) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if ((i->type != Scope::eIf && i->type != Scope::eWhile) || !i->classDef) continue; for (const Token *tok = i->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 = _settings->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 = _settings->library.detectContainer(tok->variable()->typeStartToken()); else { // Container of container - find the inner container container = _settings->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 = _settings->library.detectContainer(tok2); // innner container } else container = nullptr; } } if (container && container->getAction(funcTok->str()) == Library::Container::FIND) { if (if_findCompare(funcTok->next())) continue; if (printWarning && container->getYield(funcTok->str()) == Library::Container::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) reportError(tok, Severity::performance, "stlIfStrFind", "Inefficient usage of string::find() in condition; string::compare() would be faster.\n" "Either inefficient or wrong usage of string::find(). string::compare() 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); else reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find() is an iterator, but it is not properly checked.", 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 (!_settings->isEnabled(Settings::PERFORMANCE)) return; if (_settings->standards.cpp == Standards::CPP11) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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", "Possible inefficient checking for '" + varname + "' emptiness.\n" "Checking for '" + varname + "' emptiness might be inefficient. " "Using " + varname + ".empty() instead of " + varname + ".size() can be faster. " + varname + ".size() can take linear time but " + varname + ".empty() is " "guaranteed to take constant time.", CWE398, false); } void CheckStl::redundantCondition() { if (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eIf) continue; const Token* tok = i->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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eFor || !i->classDef) continue; for (const Token *tok2 = i->classDef->tokAt(2); tok2 != i->classStart; 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 unsigned 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 = i->classStart; tok3 != i->classEnd; 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; callstack.push_back(incrementToken1); callstack.push_back(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 = make_container< std::set >() << "istringstream" << "ostringstream" << "stringstream" << "wstringstream" ; } void CheckStl::string_c_str() { const bool printInconclusive = _settings->inconclusive; const bool printPerformance = _settings->isEnabled(Settings::PERFORMANCE); const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); // Find all functions that take std::string as argument std::multimap c_strFuncParam; if (printPerformance) { for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 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; } unsigned 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 (std::list::const_iterator var = func->argumentList.cbegin(); var != func->argumentList.cend(); ++var) { 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 (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 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->classStart; tok && tok != scope->classEnd; 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 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); unsigned 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, unsigned 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); } static bool hasArrayEnd(const Token *tok1) { const Token *end = Token::findsimplematch(tok1, ";"); return (end && Token::simpleMatch(end->previous(), "] ;")); } static bool hasArrayEndParen(const Token *tok1) { const Token *end = Token::findsimplematch(tok1, ";"); return (end && end->previous() && Token::simpleMatch(end->tokAt(-2), "] ) ;")); } //--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- void CheckStl::checkAutoPointer() { std::set autoPtrVarId; std::map mallocVarId; // variables allocated by the malloc-like function const char STL_CONTAINER_LIST[] = "array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string"; const int malloc = _settings->library.allocId("malloc"); // allocation function, which are not compatible with auto_ptr const bool printStyle = _settings->isEnabled(Settings::STYLE); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "auto_ptr <")) { if ((tok->strAt(-1) == "<" && Token::Match(tok->tokAt(-2), STL_CONTAINER_LIST)) || (Token::simpleMatch(tok->tokAt(-3), "< std :: auto_ptr") && Token::Match(tok->tokAt(-4), STL_CONTAINER_LIST))) { autoPointerContainerError(tok); } else { const Token *tok2 = tok->linkAt(1); if (Token::Match(tok2, "> %name%")) { const Token *tok3 = tok2->tokAt(2); if (Token::Match(tok3, "( new %type%") && hasArrayEndParen(tok3)) { autoPointerArrayError(tok2->next()); } if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next(), -1) == malloc) { // malloc-like function allocated memory passed to the auto_ptr constructor -> error autoPointerMallocError(tok2->next(), tok3->next()->str()); } if (Token::Match(tok3, "( %var%")) { std::map::const_iterator it = mallocVarId.find(tok3->next()->varId()); if (it != mallocVarId.cend()) { // pointer on the memory allocated by malloc used in the auto pointer constructor -> error autoPointerMallocError(tok2->next(), it->second); } } while (tok3 && tok3->str() != ";") { tok3 = tok3->next(); } if (tok3) { tok3 = tok3->tokAt(-2); if (Token::simpleMatch(tok3->previous(), "[ ] )")) { autoPointerArrayError(tok2->next()); } else if (tok3->varId()) { const Token *decltok = Token::findmatch(_tokenizer->tokens(), "%varid% = new %type%", tok3->varId()); if (decltok && hasArrayEnd(decltok)) { autoPointerArrayError(tok2->next()); } } if (tok2->next()->varId()) { autoPtrVarId.insert(tok2->next()->varId()); } } } } } else { if (Token::Match(tok, "%name% = %var% ;")) { if (printStyle) { std::set::const_iterator iter = autoPtrVarId.find(tok->tokAt(2)->varId()); if (iter != autoPtrVarId.end()) { autoPointerError(tok->tokAt(2)); } } } else if ((Token::Match(tok, "%var% = new %type%") && hasArrayEnd(tok)) || (Token::Match(tok, "%var% . reset ( new %type%") && hasArrayEndParen(tok))) { std::set::const_iterator iter = autoPtrVarId.find(tok->varId()); if (iter != autoPtrVarId.end()) { autoPointerArrayError(tok); } } else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2), -1) == malloc) { // C library function like 'malloc' used together with auto pointer -> error std::set::const_iterator iter = autoPtrVarId.find(tok->varId()); if (iter != autoPtrVarId.end()) { autoPointerMallocError(tok, tok->strAt(2)); } else if (tok->varId()) { // it is not an auto pointer variable and it is allocated by malloc like function. mallocVarId.insert(std::make_pair(tok->varId(), tok->strAt(2))); } } else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4), -1) == malloc) { // C library function like 'malloc' used when resetting auto pointer -> error std::set::const_iterator iter = autoPtrVarId.find(tok->varId()); if (iter != autoPtrVarId.end()) { autoPointerMallocError(tok, tok->strAt(4)); } } } } } void CheckStl::autoPointerError(const Token *tok) { reportError(tok, Severity::style, "useAutoPointerCopy", "Copying 'auto_ptr' pointer to another does not create two equal objects since one has lost its ownership of the pointer.\n" "'std::auto_ptr' has semantics of strict ownership, meaning that the 'auto_ptr' instance is the sole entity responsible for the object's lifetime. If an 'auto_ptr' is copied, the source looses the reference.", CWE398, false); } void CheckStl::autoPointerContainerError(const Token *tok) { reportError(tok, Severity::error, "useAutoPointerContainer", "You can randomly lose access to pointers if you store 'auto_ptr' pointers in an STL container.\n" "An element of container must be able to be copied but 'auto_ptr' does not fulfill this requirement. You should consider to use 'shared_ptr' or 'unique_ptr'. It is suitable for use in containers, because they no longer copy their values, they move them.", CWE664, false ); } void CheckStl::autoPointerArrayError(const Token *tok) { reportError(tok, Severity::error, "useAutoPointerArray", "Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n" "Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes arrays, which are allocated by operator 'new[]' and must be deallocated by operator 'delete[]'.", CWE664, false ); } void CheckStl::autoPointerMallocError(const Token *tok, const std::string& allocFunction) { const std::string summary = "Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function '" + allocFunction + "'."; const std::string verbose = summary + " This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes use C library allocation functions (for example '" + allocFunction + "'), which must be deallocated by the appropriate C library function."; reportError(tok, Severity::error, "useAutoPointerMalloc", summary + "\n" + verbose, CWE762, false); } namespace { const std::set stl_containers_with_empty_and_clear = make_container< std::set >() << "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 = _settings->isEnabled(Settings::PERFORMANCE); const bool printWarning = _settings->isEnabled(Settings::WARNING); if (!printPerformance && !printWarning) return; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart; tok != scope->classEnd; 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->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 << "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) { std::ostringstream errmsg; errmsg << "It is inefficient to swap a object with itself by calling '" << varname << ".swap(" << varname << ")'\n" << "The 'swap()' function has no logical effect when given itself as parameter " << "(" << varname << ".swap(" << varname << ")). As it is currently the " << "code is inefficient. Is the object or the parameter wrong here?"; reportError(tok, Severity::performance, "uselessCallsSwap", errmsg.str(), 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", "Return value of std::" + function + "() ignored. Elements remain in container.\n" "The return value of std::" + function + "() 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 (!_settings->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. const std::list& scopeList = _tokenizer->getSymbolDatabase()->scopeList; for (std::list::const_iterator i = scopeList.begin(); i != scopeList.end(); ++i) { if (!(i->type == Scope::eIf || i->type == Scope::eDo || i->type == Scope::eWhile || i->type == Scope::eFor)) continue; const Token* const tok = i->classDef; const Token* startOfCondition = tok->next(); if (i->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 (i->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 unsigned 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", "Possible dereference of an invalid iterator: " + iterName + "\n" + "Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, false); } void CheckStl::readingEmptyStlContainer_parseUsage(const Token* tok, const Library::Container* container, std::map& empty, bool noerror) { // Check for various conditions for the way stl containers and variables can be used if (tok->strAt(1) == "=" || (tok->strAt(1) == "[" && Token::simpleMatch(tok->linkAt(1), "] ="))) { // Assignment (LHS) empty.erase(tok->varId()); } else if (Token::Match(tok, "%name% [")) { // Access through operator[] if (!container->arrayLike_indexOp) { // operator[] inserts an element if used on a std::map if (!noerror && tok->strAt(-1) == "=") readingEmptyStlContainerError(tok); empty.erase(tok->varId()); } else if (!noerror) readingEmptyStlContainerError(tok); } else if (Token::Match(tok, "%name% . %type% (")) { // Member function call const Library::Container::Action action = container->getAction(tok->strAt(2)); if ((action == Library::Container::FIND || action == Library::Container::ERASE || action == Library::Container::POP || action == Library::Container::CLEAR) && !noerror) { readingEmptyStlContainerError(tok); return; } const Token* parent = tok->tokAt(3)->astParent(); const Library::Container::Yield yield = container->getYield(tok->strAt(2)); bool yieldsIterator = (yield == Library::Container::ITERATOR || yield == Library::Container::START_ITERATOR || yield == Library::Container::END_ITERATOR); if (yield != Library::Container::NO_YIELD && (!parent || Token::Match(parent, "%cop%|*") || parent->isAssignmentOp() || !yieldsIterator)) { // These functions read from the container if (!noerror && (!yieldsIterator || !parent || !parent->isAssignmentOp())) readingEmptyStlContainerError(tok); } else empty.erase(tok->varId()); } else if (tok->strAt(-1) == "=") { // Assignment (RHS) if (!noerror) readingEmptyStlContainerError(tok); } else { // Unknown usage. Assume it is initialized. empty.erase(tok->varId()); } } void CheckStl::readingEmptyStlContainer() { if (!_settings->isEnabled(Settings::STYLE)) return; if (!_settings->inconclusive) return; std::map emptyContainer; const std::list& scopeList = _tokenizer->getSymbolDatabase()->scopeList; for (std::list::const_iterator i = scopeList.begin(); i != scopeList.end(); ++i) { if (i->type != Scope::eFunction) continue; for (const Token *tok = i->classStart->next(); tok != i->classEnd; tok = tok->next()) { if (Token::Match(tok, "for|while")) { // Loops and end of scope clear the sets. const Token* tok2 = tok->linkAt(1); if (!tok2) continue; tok2 = tok2->next(); for (const Token* end2 = tok2->link(); tok2 && tok2 != end2; tok2 = tok2->next()) { if (!tok2->varId()) continue; const std::map::const_iterator container = emptyContainer.find(tok2->varId()); if (container == emptyContainer.end()) continue; readingEmptyStlContainer_parseUsage(tok2, container->second, emptyContainer, true); } } else if (Token::Match(tok, "do|}|break|case")) { emptyContainer.clear(); } else if (tok->str() == "{" && tok->next()->scope()->type == Scope::eLambda) tok = tok->link(); // function call if (Token::Match(tok, "!!. %name% (") && !Token::simpleMatch(tok->linkAt(2), ") {")) { for (std::map::iterator it = emptyContainer.begin(); it != emptyContainer.end();) { const Variable *var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(it->first); if (var && (var->isLocal() || var->isArgument())) ++it; else emptyContainer.erase(it++); } } if (!tok->varId()) continue; // Check whether a variable should be marked as "empty" const Variable* var = tok->variable(); if (var && !var->isArrayOrPointer() && !var->typeStartToken()->isStandardType()) { bool insert = false; if (var->nameToken() == tok && var->isLocal() && !var->isStatic()) { // Local variable declared insert = !Token::Match(tok->next(), "[(=:]"); // Only if not initialized } else if (Token::Match(tok, "%var% . clear ( ) ;")) { insert = true; } if (insert) { const Library::Container* container = _settings->library.detectContainer(var->typeStartToken()); if (container) emptyContainer[var->declarationId()] = container; continue; } } const std::map::const_iterator container = emptyContainer.find(tok->varId()); if (container == emptyContainer.end()) continue; readingEmptyStlContainer_parseUsage(tok, container->second, emptyContainer, false); } emptyContainer.clear(); } } void CheckStl::readingEmptyStlContainerError(const Token *tok) { reportError(tok, Severity::style, "reademptycontainer", "Reading from empty STL container '" + (tok ? tok->str() : std::string("var")) + "'", CWE398, true); } cppcheck-1.82/lib/checkstl.h000066400000000000000000000225511322667425100157600ustar00rootroot00000000000000/* * 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 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) { } /** Simplified checks. The token list is simplified. */ void runSimplifiedChecks(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) { if (!tokenizer->isCPP()) { return; } CheckStl checkStl(tokenizer, settings, errorLogger); checkStl.stlOutOfBounds(); checkStl.negativeIndex(); checkStl.iterators(); checkStl.mismatchingContainers(); checkStl.erase(); checkStl.pushback(); checkStl.stlBoundaries(); checkStl.if_find(); checkStl.string_c_str(); checkStl.checkAutoPointer(); checkStl.uselessCalls(); checkStl.checkDereferenceInvalidIterator(); // Style check checkStl.size(); checkStl.redundantCondition(); checkStl.missingComparison(); checkStl.readingEmptyStlContainer(); } /** * 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(); /** * 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); /** * Dangerous usage of push_back and insert */ void pushback(); /** * bad condition.. "it < alist.end()" */ void stlBoundaries(); /** if (a.find(x)) - possibly incorrect condition */ void if_find(); /** * 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 for use and copy auto pointer */ void checkAutoPointer(); /** @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 */ void readingEmptyStlContainer(); private: void readingEmptyStlContainer_parseUsage(const Token* tok, const Library::Container* container, std::map& empty, bool noerror); 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, unsigned int number); 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& container1, const std::string& container2); void mismatchingContainersError(const Token* tok); void invalidIteratorError(const Token* tok, const std::string& func, const std::string& iterator_name); void invalidPointerError(const Token* tok, const std::string& func, const std::string& pointer_name); void stlBoundariesError(const Token* tok); void if_findError(const Token* tok, bool str); void sizeError(const Token* tok); void redundantIfRemoveError(const Token* tok); void autoPointerError(const Token* tok); void autoPointerContainerError(const Token* tok); void autoPointerArrayError(const Token* tok); void autoPointerMallocError(const Token* tok, const std::string& allocFunction); 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); void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const { CheckStl c(nullptr, settings, errorLogger); c.invalidIteratorError(nullptr, "iterator"); c.iteratorsError(nullptr, "container1", "container2"); c.mismatchingContainersError(nullptr); c.dereferenceErasedError(nullptr, nullptr, "iter", false); c.stlOutOfBoundsError(nullptr, "i", "foo", false); c.negativeIndexError(nullptr, ValueFlow::Value(-1)); c.invalidIteratorError(nullptr, "push_back|push_front|insert", "iterator"); c.invalidPointerError(nullptr, "push_back", "pointer"); c.stlBoundariesError(nullptr); c.if_findError(nullptr, false); c.if_findError(nullptr, true); c.string_c_strError(nullptr); c.string_c_strReturn(nullptr); c.string_c_strParam(nullptr, 0); c.sizeError(nullptr); c.missingComparisonError(nullptr, nullptr); c.redundantIfRemoveError(nullptr); c.autoPointerError(nullptr); c.autoPointerContainerError(nullptr); c.autoPointerArrayError(nullptr); c.autoPointerMallocError(nullptr, "malloc"); 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); } static std::string myName() { return "STL usage"; } std::string classInfo() const { 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" "- 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" "- redundant condition\n" "- common mistakes when using string::c_str()\n" "- using auto pointer (auto_ptr)\n" "- useless calls of string and STL functions\n" "- dereferencing an invalid iterator\n" "- reading from empty STL container\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkstlH cppcheck-1.82/lib/checkstring.cpp000066400000000000000000000532071322667425100170210ustar00rootroot00000000000000/* * 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 "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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!tok->variable() || !tok->variable()->isPointer()) continue; const Token *str = tok->getValueTokenMinStrSize(); 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->strValue(); if (s.size() > 15U) s = s.substr(0,13) + ".."; 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 (!_settings->isEnabled(Settings::WARNING)) return; for (const Token* tok = _tokenizer->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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->tokType() != Token::eComparisonOp) 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() == "+" && _tokenizer->isC()) { const Token *tokens[2] = { varTok->astOperand1(), varTok->astOperand2() }; for (int nr = 0; nr < 2; nr++) { const Token *t = tokens[nr]; while (t && (t->str() == "." || t->str() == "::")) t = t->astOperand2(); if (t && t->variable() && t->variable()->isPointer()) varTok = t; } } if (varTok->str() == "*") { if (!_tokenizer->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 (_tokenizer->isC() || (var && var->isArrayOrPointer())) suspiciousStringCompareError(tok, varname); } else if (ischar && var && var->isPointer()) { suspiciousStringCompareError_char(tok, varname); } } } } void CheckString::suspiciousStringCompareError(const Token* tok, const std::string& var) { reportError(tok, Severity::warning, "literalWithCharPtrCompare", "String literal compared with variable '" + var + "'. Did you intend to use strcmp() instead?", CWE595, false); } void CheckString::suspiciousStringCompareError_char(const Token* tok, const std::string& var) { reportError(tok, Severity::warning, "charLiteralWithCharPtrCompare", "Char literal compared with pointer '" + var + "'. 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"); } void CheckString::strPlusChar() { const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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) { reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type 'char' 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 (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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% )")) { 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) != "+") { std::size_t slen = Token::getStrLength(begin->previous()); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", begin->strAt(-1)); } } else if (Token::Match(end, "==|!= %str% !!+")) { 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% &&|%oror%|)") && !Token::Match(tok, "( %str% )")) { incorrectStringBooleanError(tok->next(), tok->strAt(1)); } else if (Token::Match(tok, "if|while ( %str% )")) { incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2)); } } } } void CheckString::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) { reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "().", CWE570, false); } void CheckString::incorrectStringBooleanError(const Token *tok, const std::string& string) { reportError(tok, Severity::warning, "incorrectStringBooleanError", "Conversion of string literal " + string + " to bool always evaluates to true.", CWE571, false); } //--------------------------------------------------------------------------- // always true: strcmp(str,"a")==0 || strcmp(str,"b") // TODO: Library configuration for string comparison functions //--------------------------------------------------------------------------- void CheckString::overlappingStrcmp() { if (!_settings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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); } bool error = false; for (std::list::const_iterator eq0 = equals0.begin(); !error && eq0 != equals0.end(); ++eq0) { for (std::list::const_iterator ne0 = notEquals0.begin(); !error && ne0 != notEquals0.end(); ++ne0) { const Token *tok1 = *eq0; const Token *tok2 = *ne0; if (!Token::simpleMatch(tok1->previous(), "strcmp (")) continue; if (!Token::simpleMatch(tok2->previous(), "strcmp (")) continue; const std::vector args1 = getArguments(tok1->previous()); const std::vector args2 = getArguments(tok2->previous()); if (args1.size() != 2 || args2.size() != 2) continue; if (args1[1]->isLiteral() && args2[1]->isLiteral() && args1[1]->str() != args2[1]->str() && isSameExpression(_tokenizer->isCPP(), true, args1[0], args2[0], _settings->library, true)) overlappingStrcmpError(tok1, tok2); } } } } } 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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; 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) { bool same = isSameExpression(_tokenizer->isCPP(), false, args[0], args[argnr], _settings->library, true); if (same) { sprintfOverlappingDataError(args[argnr], args[argnr]->expressionString()); } } } } } void CheckString::sprintfOverlappingDataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "sprintfOverlappingData", "Undefined behavior: Variable '" + varname + "' is used as parameter and destination in s[n]printf().\n" "The variable '" + varname + "' is used both as a parameter and as 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.\"", CWE628, false); } cppcheck-1.82/lib/checkstring.h000066400000000000000000000127651322667425100164720ustar00rootroot00000000000000/* * 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 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) { CheckString checkString(tokenizer, settings, errorLogger); // Checks checkString.strPlusChar(); checkString.checkSuspiciousStringCompare(); checkString.stringLiteralWrite(); checkString.overlappingStrcmp(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckString checkString(tokenizer, settings, errorLogger); // Checks checkString.checkIncorrectStringCompare(); checkString.checkAlwaysTrueOrFalseStringCompare(); checkString.sprintfOverlappingData(); } /** @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 *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); void suspiciousStringCompareError_char(const Token* tok, const std::string& var); void overlappingStrcmpError(const Token* tok1, const Token *tok2); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckString c(nullptr, settings, errorLogger); c.stringLiteralWriteError(nullptr, nullptr); c.sprintfOverlappingDataError(nullptr, "varname"); c.strPlusCharError(nullptr); c.incorrectStringCompareError(nullptr, "substr", "\"Hello World\""); c.suspiciousStringCompareError(nullptr, "foo"); c.suspiciousStringCompareError_char(nullptr, "foo"); c.incorrectStringBooleanError(nullptr, "\"Hello World\""); c.alwaysTrueFalseStringCompareError(nullptr, "str1", "str2"); c.alwaysTrueStringVariableCompareError(nullptr, "varname1", "varname2"); c.overlappingStrcmpError(nullptr, nullptr); } static std::string myName() { return "String"; } std::string classInfo() const { 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 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.82/lib/checktype.cpp000066400000000000000000000401301322667425100164630ustar00rootroot00000000000000/* * 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 "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 (_settings->platformType == Settings::Unspecified) return; for (const Token *tok = _tokenizer->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 *lhstype = tok->astOperand1()->valueType(); if (!lhstype || !lhstype->isIntegral() || lhstype->pointer >= 1U) continue; int lhsbits = 0; if (lhstype->type <= ValueType::Type::INT) lhsbits = _settings->int_bit; else if (lhstype->type == ValueType::Type::LONG) lhsbits = _settings->long_bit; else if (lhstype->type == ValueType::Type::LONGLONG) lhsbits = _settings->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, _settings); if (value && _settings->isEnabled(value, false)) tooBigBitwiseShiftError(tok, lhsbits, *value); else if (lhstype->sign == ValueType::Sign::SIGNED) { value = tok->astOperand2()->getValueGE(lhsbits-1, _settings); if (value && _settings->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"; if (!tok) { reportError(tok, Severity::error, id, "Shifting signed 32-bit value by 31 bits is undefined 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 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()); } //--------------------------------------------------------------------------- // Checking for integer overflow //--------------------------------------------------------------------------- void CheckType::checkIntegerOverflow() { // unknown sizeof(int) => can't run this checker if (_settings->platformType == Settings::Unspecified || _settings->int_bit >= MathLib::bigint_bits) return; for (const Token *tok = _tokenizer->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 = _settings->int_bit; else if (vt->type == ValueType::Type::LONG) bits = _settings->long_bit; else if (vt->type == ValueType::Type::LONGLONG) bits = _settings->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, _settings); if (!value) value = tok->getValueLE(-maxvalue - 2, _settings); if (!value || !_settings->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 + "'."; reportError(getErrorPath(tok, &value, "Integer overflow"), value.errorSeverity() ? Severity::error : Severity::warning, "integerOverflow", msg, CWE190, value.isInconclusive()); } //--------------------------------------------------------------------------- // Checking for sign conversion when operand can be negative //--------------------------------------------------------------------------- void CheckType::checkSignConversion() { if (!_settings->isEnabled(Settings::WARNING)) return; for (const Token *tok = _tokenizer->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; if (!tok1->getValueLE(-1,_settings)) continue; if (tok1->valueType() && tok1->valueType()->sign != ValueType::Sign::UNSIGNED) signConversionError(tok1, tok1->isNumber()); } } } void CheckType::signConversionError(const Token *tok, const bool constvalue) { const std::string varname(tok ? tok->str() : "var"); reportError(tok, Severity::warning, "signConversion", (constvalue) ? "Suspicious code: sign conversion of " + varname + " in calculation because '" + varname + "' has a negative value" : "Suspicious code: sign conversion of " + varname + " in calculation, even though " + varname + " can have a negative value", CWE195, false); } //--------------------------------------------------------------------------- // Checking for long cast of int result const long x = var1 * var2; //--------------------------------------------------------------------------- void CheckType::checkLongCast() { if (!_settings->isEnabled(Settings::STYLE)) return; // Assignments.. for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "=" || !Token::Match(tok->astOperand2(), "*|<<")) 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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // 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->classStart; tok != scope->classEnd; 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 = _tokenizer->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(); } // Assignment else if (tok->str() == "=" && tok->astOperand1() && tok->astOperand2()) { vtint = tok->astOperand1()->valueType(); vtfloat = tok->astOperand2()->valueType(); floatValues = &tok->astOperand2()->values(); } // TODO: function call else continue; // Conversion of float to integer? if (!vtint || !vtint->isIntegral()) continue; if (!vtfloat || !vtfloat->isFloat()) continue; for (std::list::const_iterator it = floatValues->begin(); it != floatValues->end(); ++it) { if (it->valueType != ValueFlow::Value::FLOAT) continue; if (!_settings->isEnabled(&(*it), false)) continue; if (it->floatValue > ~0ULL) floatToIntegerOverflowError(tok, *it); else if ((-it->floatValue) > (1ULL<<62)) floatToIntegerOverflowError(tok, *it); else if (_settings->platformType != Settings::Unspecified) { int bits = 0; if (vtint->type == ValueType::Type::CHAR) bits = _settings->char_bit; else if (vtint->type == ValueType::Type::SHORT) bits = _settings->short_bit; else if (vtint->type == ValueType::Type::INT) bits = _settings->int_bit; else if (vtint->type == ValueType::Type::LONG) bits = _settings->long_bit; else if (vtint->type == ValueType::Type::LONGLONG) bits = _settings->long_long_bit; else continue; if (bits < MathLib::bigint_bits && it->floatValue >= (((MathLib::biguint)1) << bits)) floatToIntegerOverflowError(tok, *it); } } } } 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.82/lib/checktype.h000066400000000000000000000111641322667425100161350ustar00rootroot00000000000000/* * 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 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) { // 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 Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { (void)tokenizer; (void)settings; (void)errorLogger; } /** @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(); 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 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 { 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, false); c.longCastAssignError(nullptr); c.longCastReturnError(nullptr); ValueFlow::Value f; f.valueType = ValueFlow::Value::FLOAT; f.floatValue = 1E100; c.floatToIntegerOverflowError(nullptr, f); } static std::string myName() { return "Type"; } std::string classInfo() const { 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.82/lib/checkuninitvar.cpp000066400000000000000000001476471322667425100175460ustar00rootroot00000000000000/* * 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 "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 //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUninitVar instance; } //--------------------------------------------------------------------------- // CWE ids used: static const struct CWE CWE676(676U); static const struct CWE CWE908(908U); static const struct CWE CWE825(825U); void CheckUninitVar::check() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; std::set arrayTypeDefs; for (const Token *tok = _tokenizer->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 (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (scope->isExecutable()) { checkScope(&*scope, arrayTypeDefs); } } } void CheckUninitVar::checkScope(const Scope* scope, const std::set &arrayTypeDefs) { for (std::list::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { if ((_tokenizer->isCPP() && i->type() && !i->isPointer() && i->type()->needInitialization != Type::True) || i->isStatic() || i->isExtern() || i->isReference()) continue; // don't warn for try/catch exception variable if (i->isThrow()) continue; if (Token::Match(i->nameToken()->next(), "[({:]")) continue; if (Token::Match(i->nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not checkRhs(i->nameToken(), *i, NO_ALLOC, 0U, emptyString); continue; } if (Token::Match(i->nameToken(), "%name% ) (") && Token::simpleMatch(i->nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not checkRhs(i->nameToken()->linkAt(2)->next(), *i, NO_ALLOC, 0U, emptyString); continue; } if (i->isArray() || i->isPointerToArray()) { const Token *tok = i->nameToken()->next(); if (i->isPointerToArray()) tok = tok->next(); while (Token::simpleMatch(tok->link(), "] [")) tok = tok->link()->next(); if (Token::Match(tok->link(), "] =|{")) continue; } bool stdtype = _tokenizer->isC() && arrayTypeDefs.find(i->typeStartToken()->str()) == arrayTypeDefs.end(); const Token* tok = i->typeStartToken(); for (; tok != i->nameToken() && tok->str() != "<"; tok = tok->next()) { if (tok->isStandardType() || tok->isEnumType()) stdtype = true; } if (i->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(), *i, i->isArray() ? ARRAY : NO_ALLOC, emptyString, true)) continue; if (i->isArray()) { Alloc alloc = ARRAY; std::map variableValue; checkScopeForVariable(tok, *i, nullptr, nullptr, &alloc, emptyString, variableValue); continue; } if (stdtype || i->isPointer()) { Alloc alloc = NO_ALLOC; std::map variableValue; checkScopeForVariable(tok, *i, nullptr, nullptr, &alloc, emptyString, variableValue); } if (i->type()) checkStruct(tok, *i); } if (scope->function) { for (unsigned int i = 0; i < scope->function->argCount(); i++) { const Variable *arg = scope->function->getArgumentVar(i); if (arg && arg->declarationId() && Token::Match(arg->typeStartToken(), "%type% * %name% [,)]")) { // Treat the pointer as initialized until it is assigned by malloc for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "[;{}] %varid% = %name% (", arg->declarationId()) && _settings->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; 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 = _tokenizer->getSymbolDatabase(); for (std::size_t j = 0U; j < symbolDatabase->classAndStructScopes.size(); ++j) { const Scope *scope2 = symbolDatabase->classAndStructScopes[j]; if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) { for (std::list::const_iterator it = scope2->varlist.begin(); it != scope2->varlist.end(); ++it) { const Variable &var = *it; if (var.hasDefault() || var.isArray() || (!_tokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::True))) continue; // is the variable declared in a inner union? bool innerunion = false; for (std::list::const_iterator it2 = symbolDatabase->scopeList.begin(); it2 != symbolDatabase->scopeList.end(); ++it2) { const Scope &innerScope = *it2; if (innerScope.type == Scope::eUnion && innerScope.nestedIn == scope2) { if (var.typeStartToken()->linenr() >= innerScope.classStart->linenr() && var.typeStartToken()->linenr() <= innerScope.classEnd->linenr()) { innerunion = true; break; } } } if (!innerunion) { Alloc alloc = NO_ALLOC; const Token *tok2 = tok; if (tok->str() == "}") tok2 = tok2->next(); 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->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->values().size() == 1U && tok->values().front().isKnown()) { 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); const bool printDebug = _settings->debugwarnings; if (possibleInit) *possibleInit = false; unsigned 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 (_tokenizer->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; 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 unsigned int condVarId = 0; VariableValue condVarValue(0); const Token *condVarTok = nullptr; if (alwaysFalse) ; else if (Token::simpleMatch(tok, "if (") && astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) { 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) { 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; } // 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() == "{") { 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 (Token::Match(tok, "%name% . %name% ;|%cop%") && tok->strAt(2) == membervar) uninitStructMemberError(tok, tok->str() + "." + membervar); else return true; } // Use variable else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) uninitvarError(tok, tok->str(), *alloc); else // assume that variable is assigned 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::Match(tok->next(), "= %name% (") && Token::simpleMatch(tok->linkAt(3), ") ;") && _settings->library.returnuninitdata.count(tok->strAt(2)) > 0U) { *alloc = NO_CTOR_CALL; continue; } if (var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::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% [")) { const Token* tokClosingBracket=tok->linkAt(4); // array new with initialization if (tokClosingBracket && Token::simpleMatch(tokClosingBracket->next(), "( )")) 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); else if (Token::Match(tok->previous(), "[(,] %name% [,)]")) return true; } else { // Use variable if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) uninitvarError(tok, tok->str(), *alloc); 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, unsigned int number_of_if, const std::string &membervar) { bool rhs = false; unsigned 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) const { if (alloc == NO_ALLOC && ((Token::Match(vartok->previous(), "return|delete") && vartok->strAt(1) != "=") || (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 (vartok->strAt(1) == "." && vartok->strAt(-1) != "&") { // Is struct member passed to function? if (!pointer && Token::Match(vartok->previous(), "[,(] %name% . %name%")) { // TODO: there are FN currently: // - should only return false if struct member is (or might be) array. // - should only return false if function argument is (or might be) non-const pointer or reference const Token *tok2 = vartok->next(); do { tok2 = tok2->tokAt(2); } while (Token::Match(tok2, ". %name%")); if (Token::Match(tok2, "[,)]")) return false; } else if (pointer && 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 (!_settings->library.isFunctionConst(parent->strAt(-1), true)) { assignment = true; break; } } parent = parent->astParent(); } if (!assignment) return true; } // Passing variable to function.. if (Token::Match(vartok->previous(), "[(,] %name% [,)]") || Token::Match(vartok->tokAt(-2), "[(,] & %name% [,)]")) { const int use = isFunctionParUsage(vartok, pointer, alloc); if (use >= 0) return (use>0); } if (Token::Match(vartok->previous(), "++|--|%cop%")) { if (_tokenizer->isCPP() && alloc == ARRAY && Token::Match(vartok->tokAt(-4), "& %var% =|( *")) return false; if (_tokenizer->isCPP() && Token::Match(vartok->previous(), ">>|<<")) { const Token* tok2 = vartok->previous(); if (Token::simpleMatch(tok2->astOperand1(), ">>")) return false; // Looks like stream operator, initializes the variable if (Token::simpleMatch(tok2, "<<")) { // 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(), "= %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)) { // 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 (_tokenizer->isCPP() && Token::Match(vartok->next(), "<<|>>")) { // Is this calculation done in rhs? const Token *tok = vartok; while (Token::Match(tok, "%name%|.|::")) tok = tok->previous(); if (Token::Match(tok, "[;{}]")) return false; // Is variable a known POD type then this is a variable usage, // otherwise we assume it's not. const Variable *var = vartok->variable(); return (var && (var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType())); } if (alloc == NO_ALLOC && vartok->next() && vartok->next()->isOp() && !vartok->next()->isAssignmentOp()) 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) const { if (!Token::Match(vartok->previous(), "[(,]") && !Token::Match(vartok->tokAt(-2), "[(,] &")) return -1; // locate start parentheses in function call.. unsigned 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(), ") {")) 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% [,)]")) 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 = _settings->library.isnullargbad(start->previous(), argumentNumber + 1); if (pointer && !address && isnullbad && alloc == NO_ALLOC) return 1; const bool isuninitbad = _settings->library.isuninitargbad(start->previous(), argumentNumber + 1); 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 (Token::Match(tok->tokAt(-2), "%name% >>") && Token::Match(tok->tokAt(3), ";|>>")) // #6680 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.. unsigned 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(); 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) { 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())) 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 (_settings->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", "Dangerous usage of '" + varname + "'" + (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", "Memory is allocated but not initialized: " + varname, CWE908, false); } void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "uninitvar", "Uninitialized variable: " + varname, CWE908, false); } void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) { reportError(tok, Severity::error, "uninitStructMember", "Uninitialized struct member: " + membername, CWE908, false); } void CheckUninitVar::valueFlowUninit() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; // check every executable scope for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (!scope->isExecutable()) continue; for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { tok = tok->linkAt(1); continue; } if (!tok->variable() || tok->values().size() != 1U) continue; const ValueFlow::Value &v = tok->values().front(); if (v.valueType != ValueFlow::Value::UNINIT || v.isInconclusive()) continue; if (!isVariableUsage(tok, tok->variable()->isPointer(), NO_ALLOC)) continue; uninitvarError(tok, tok->str()); } } } void CheckUninitVar::deadPointer() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; // check every executable scope for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (!scope->isExecutable()) continue; // Dead pointers.. for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (tok->variable() && tok->variable()->isPointer() && isVariableUsage(tok, true, NO_ALLOC)) { const Token *alias = tok->getValueTokenDeadPointer(); if (alias) { deadPointerError(tok,alias); } } } } } void CheckUninitVar::deadPointerError(const Token *pointer, const Token *alias) { const std::string strpointer(pointer ? pointer->str() : std::string("pointer")); const std::string stralias(alias ? alias->expressionString() : std::string("&x")); reportError(pointer, Severity::error, "deadpointer", "Dead pointer usage. Pointer '" + strpointer + "' is dead if it has been assigned '" + stralias + "' at line " + MathLib::toString(alias ? alias->linenr() : 0U) + ".", CWE825, false); } cppcheck-1.82/lib/checkuninitvar.h000066400000000000000000000125001322667425100171660ustar00rootroot00000000000000/* * 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 checkuninitvarH #define checkuninitvarH //--------------------------------------------------------------------------- #include "check.h" #include "config.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 simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); checkUninitVar.check(); checkUninitVar.deadPointer(); 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, unsigned int number_of_if, const std::string &membervar); bool isVariableUsage(const Token *vartok, bool pointer, Alloc alloc) const; int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc) 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 dead pointer usage */ void deadPointer(); void deadPointerError(const Token *pointer, const Token *alias); /** ValueFlow-based checking for uninitialized variables */ void valueFlowUninit(); /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /* functions that must have initialized data */ std::set uvarFunctions; /* functions calls with uninitialized data */ std::set functionCalls; }; 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); 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: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckUninitVar c(nullptr, settings, errorLogger); // error c.uninitstringError(nullptr, "varname", true); c.uninitdataError(nullptr, "varname"); c.uninitvarError(nullptr, "varname"); c.uninitStructMemberError(nullptr, "a.b"); c.deadPointerError(nullptr, nullptr); } static std::string myName() { return "Uninitialized variables"; } std::string classInfo() const { return "Uninitialized variables\n" "- using uninitialized local variables\n" "- using allocated data before it has been initialized\n" "- using dead pointer\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkuninitvarH cppcheck-1.82/lib/checkunusedfunctions.cpp000066400000000000000000000366701322667425100207540ustar00rootroot00000000000000/* * 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 "checkunusedfunctions.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 //--------------------------------------------------------------------------- // 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, bool clear) { const bool doMarkup = settings->library.markupFile(FileName); const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase(); // Function declarations.. if (clear) _functionDecl.clear(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); i++) { const Scope* scope = symbolDatabase->functionScopes[i]; const Function* func = scope->function; if (!func || !func->token || scope->classStart->fileIndex() != 0) continue; // Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor)) if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator()) continue; // Don't care about templates if (tokenizer.isCPP() && func->retDef->str() == "template") continue; _functionDecl.push_back(FunctionDecl(func)); FunctionUsage &usage = _functions[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.. if (clear) _functionCalls.clear(); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { // 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())) { _functionCalls.insert(markupVarToken->str()); if (_functions.find(markupVarToken->str()) != _functions.end()) _functions[markupVarToken->str()].usedOtherFile = true; else if (markupVarToken->next()->str() == "(") { FunctionUsage &func = _functions[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 (_functions.find(value) != _functions.end()) { _functions[value].usedOtherFile = true; } _functionCalls.insert(value); } if (settings->library.isexportedsuffix(tok->str(), propToken->str())) { const Token* prevPropToken = propToken->previous(); const std::string& value = prevPropToken->str(); if (value != ")" && _functions.find(value) != _functions.end()) { _functions[value].usedOtherFile = true; } _functionCalls.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()) { _functions[value].usedOtherFile = true; _functionCalls.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); _functions[value].usedOtherFile = true; _functionCalls.insert(value); } } } const Token *funcname = nullptr; if (tok->scope()->isExecutable() && Token::Match(tok, "%name% (")) { funcname = tok; } else if (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 = _functions[ 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; _functionCalls.insert(funcname->str()); } } } bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings& settings) { bool errors = false; for (std::map::const_iterator it = _functions.begin(); it != _functions.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) { 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."; _errorLogger->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, "The function '" + funcname + "' 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 std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { (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 (std::list::const_iterator it = _functionDecl.begin(); it != _functionDecl.end(); ++it) { ret << " functionName) << '\"' << " lineNumber=\"" << it->lineNumber << "\"/>\n"; } for (std::set::const_iterator it = _functionCalls.begin(); it != _functionCalls.end(); ++it) { 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 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; 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()) { const Location &loc = decl->second; unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName); } } } cppcheck-1.82/lib/checkunusedfunctions.h000066400000000000000000000102331322667425100204040ustar00rootroot00000000000000/* * 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 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) { } // 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, bool clear=true); // 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; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger); 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 { CheckUnusedFunctions c(nullptr, settings, errorLogger); c.unusedFunctionError(errorLogger, emptyString, 0, "funcName"); } /** * 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); /** * Dummy implementation, just to provide error for --errorlist */ void runSimplifiedChecks(const Tokenizer* /*tokenizer*/, const Settings* /*settings*/, ErrorLogger* /*errorLogger*/) { } static std::string myName() { return "Unused functions"; } std::string classInfo() const { 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 _functions; class CPPCHECKLIB FunctionDecl { public: explicit FunctionDecl(const Function *f); std::string functionName; unsigned int lineNumber; }; std::list _functionDecl; std::set _functionCalls; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedfunctionsH cppcheck-1.82/lib/checkunusedvar.cpp000066400000000000000000001604101322667425100175220ustar00rootroot00000000000000/* * 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 "checkunusedvar.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), _type(type), _read(read), _write(write), _modified(modified), _allocateMemory(allocateMemory) { } /** variable is used.. set both read+write */ void use(std::list > & varReadInScope) { varReadInScope.back().insert(_var->declarationId()); _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 _type; bool _read; bool _write; bool _modified; // read/modify/write bool _allocateMemory; }; class ScopeGuard { public: ScopeGuard(Variables & guarded, bool insideLoop) :_guarded(guarded), _insideLoop(insideLoop) { _guarded.enterScope(); } ~ScopeGuard() { _guarded.leaveScope(_insideLoop); } private: /** No implementation */ ScopeGuard(); ScopeGuard& operator=(const ScopeGuard &); Variables & _guarded; bool _insideLoop; }; void clear() { _varUsage.clear(); } const std::map &varUsage() const { return _varUsage; } 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) { _varUsage.erase(varid); } void eraseAliases(unsigned int varid); void eraseAll(unsigned int varid); void clearAliases(unsigned int varid); ScopeGuard newScope(bool insideLoop) { return ScopeGuard(*this, insideLoop); } private: void enterScope(); void leaveScope(bool insideLoop); std::map _varUsage; std::list > _varAddedInScope; std::list > _varReadInScope; }; /** * 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(_varReadInScope); 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->_type == Variables::pointer) { _varReadInScope.back().insert(varid2); 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) { std::set::const_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 Variable *var, VariableType type, bool write_) { if (var->declarationId() > 0) { _varAddedInScope.back().insert(var->declarationId()); _varUsage.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) { _varReadInScope.back().insert(varid); usage->_read = true; if (tok) usage->_lastAccess = tok; } } void Variables::readAliases(unsigned int varid, const Token* tok) { 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) { _varReadInScope.back().insert(*aliases); 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) { std::set::const_iterator aliases; for (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(_varReadInScope); usage->_lastAccess = tok; std::set::const_iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->use(_varReadInScope); 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; std::set::const_iterator aliases; for (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 = _varUsage.find(varid); if (i != _varUsage.end()) return &i->second; } return nullptr; } void Variables::enterScope() { _varAddedInScope.push_back(std::set()); _varReadInScope.push_back(std::set()); } void Variables::leaveScope(bool insideLoop) { if (insideLoop) { // read variables are read again in subsequent run through loop std::set const & currentVarReadInScope = _varReadInScope.back(); for (std::set::const_iterator readIter = currentVarReadInScope.begin(); readIter != currentVarReadInScope.end(); ++readIter) { read(*readIter, nullptr); } } std::list >::reverse_iterator reverseReadIter = _varReadInScope.rbegin(); ++reverseReadIter; if (reverseReadIter != _varReadInScope.rend()) { // Transfer read variables into previous scope std::set const & currentVarAddedInScope = _varAddedInScope.back(); std::set & currentVarReadInScope = _varReadInScope.back(); for (std::set::const_iterator addedIter = currentVarAddedInScope.begin(); addedIter != currentVarAddedInScope.end(); ++addedIter) { currentVarReadInScope.erase(*addedIter); } std::set & previousVarReadInScope = *reverseReadIter; previousVarReadInScope.insert(currentVarReadInScope.begin(), currentVarReadInScope.end()); } _varReadInScope.pop_back(); _varAddedInScope.pop_back(); } 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 unsigned int varid2 = tok->varId(); const Variables::VariableUsage* var2 = variables.find(varid2); if (var2) { // local variable (alias or read it) if (var1->_type == Variables::pointer || var1->_type == Variables::pointerArray) { if (dereference) variables.read(varid2, tok); else { if (addressOf || var2->_type == Variables::array || var2->_type == Variables::pointer) { bool replace = true; // pointerArray => don't replace if (var1->_type == 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->_type == Variables::reference) variables.readAliases(varid2, tok); else variables.read(varid2, tok); } else { variables.readAll(varid2, tok); } } } else if (var1->_type == Variables::reference) { variables.alias(varid1, varid2, true); } else { if ((var2->_type == Variables::pointer || var2->_type == 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->_type == 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->_type == Variables::array || var2->_type == 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->_type == Variables::array || var2->_type == 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, bool insideLoop) { Variables::ScopeGuard scopeGuard=variables.newScope(insideLoop); // 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 (_tokenizer->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->classStart->next(); else tok = scope->classDef->next(); for (; tok && tok != scope->classEnd; tok = tok->next()) { if (tok->str() == "for" || tok->str() == "while" || tok->str() == "do") { for (std::list::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classDef == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables, true); // Scan child scope tok = (*i)->classStart->link(); break; } } if (!tok) break; } if (tok->str() == "{" && tok != scope->classStart && !tok->previous()->varId()) { for (std::list::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classStart == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables, false); // Scan child scope tok = tok->link(); break; } } if (!tok) break; } if (Token::Match(tok, "asm ( %str% )")) { variables.clear(); break; } if (Token::Match(tok, "goto|break")) { // #4447 variables.clear(); break; } // templates if (tok->isName() && tok->str().back() == '>') { // 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(); } // bailout when for_each is used if (Token::Match(tok, "%name% (") && Token::simpleMatch(tok->linkAt(1), ") {") && !Token::Match(tok, "if|for|while|switch")) { // does the name contain "for_each" or "foreach"? std::string nameTok; nameTok.resize(tok->str().size()); std::transform(tok->str().begin(), tok->str().end(), nameTok.begin(), ::tolower); if (nameTok.find("foreach") != std::string::npos || nameTok.find("for_each") != std::string::npos) { // bailout all variables in the body that are used more than once. // TODO: there is no need to bailout if variable is only read or only written std::set varid; const Token * const endTok = tok->linkAt(1)->linkAt(1); for (const Token *tok2 = endTok->link(); tok2 && tok2 != endTok; tok2 = tok2->next()) { if (tok2->varId()) { if (varid.find(tok2->varId()) == varid.end()) varid.insert(tok2->varId()); else variables.erase(tok2->varId()); } } } } // C++11 std::for_each // No warning should be written if a variable is first read and // then written in the body. else if (_tokenizer->isCPP() && Token::simpleMatch(tok, "for_each (") && Token::simpleMatch(tok->linkAt(1), ") ;")) { const Token *end = tok->linkAt(1); if (end->previous()->str() == "}") { std::set readvar; for (const Token *body = end->linkAt(-1); body != end; body = body->next()) { if (body->varId() == 0U) continue; if (!Token::simpleMatch(body->next(),"=")) readvar.insert(body->varId()); else if (readvar.find(body->varId()) != readvar.end()) variables.erase(body->varId()); } } } 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% )") || (_tokenizer->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(); } Variables::VariableUsage *var = variables.find(varid); if (var && !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) { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::array) variables.write(varid1, tok); variables.writeAliases(varid1, tok); variables.read(varid1, tok); } else { Variables::VariableUsage *var = variables.find(varid1); if (var && (inwhile || start->strAt(-1) == ",")) { variables.use(varid1, tok); } else if (var && var->_type == 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->_type == 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 (_tokenizer->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->_type == 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); } } Variables::VariableUsage *var2 = variables.find(tok->varId()); if (var2) { if (var2->_type == 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->_type == 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(); Variables::VariableUsage *var = variables.find(varId); if (var && var->_type != 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->_type == 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->_type == Variables::pointer || var->_type == Variables::reference) { variables.read(varid, tok); variables.writeAliases(varid, tok); } else if (var->_type == Variables::pointerArray) { tok = doAssignment(variables, tok, deref, scope); } else variables.writeAll(varid, tok); } } else if (_tokenizer->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 (_tokenizer->isC() || (tok->previous()->variable() && tok->previous()->variable()->typeEndToken()->isStandardType() && tok->astOperand1() && tok->astOperand1()->str() != ">>")) variables.read(tok->next()->varId(), tok); else variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) { variables.read(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 (!_settings->isEnabled(Settings::STYLE)) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // only check functions const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // Bailout when there are lambdas or inline functions // TODO: Handle lambdas and inline functions properly if (scope->hasInlineOrLambdaFunction()) continue; // varId, usage {read, write, modified} Variables variables; checkFunctionVariableUsage_iterateScopes(scope, variables, false); // 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._type == Variables::pointerPointer || usage._type == Variables::pointerArray || usage._type == 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 written but not read else if (!usage._read) unreadVariableError(usage._lastAccess, varname, usage._modified); // 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", "Unused variable: " + varname, CWE563, false); } void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' 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", "Variable '" + varname + "' is modified but its new value is never used.", CWE563, false); else reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' 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", "Variable '" + varname + "' is not assigned a value.", CWE665, false); } //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- void CheckUnusedVar::checkStructMemberUsage() { if (!_settings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator scope = symbolDatabase->scopeList.cbegin(); scope != symbolDatabase->scopeList.cend(); ++scope) { if (scope->type != Scope::eStruct && scope->type != Scope::eUnion) continue; if (scope->classStart->fileIndex() != 0 || scope->className.empty()) continue; // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware. if (scope->classEnd->isAttributePacked()) continue; // Bail out if struct/union contains any functions if (!scope->functionList.empty()) continue; // bail out if struct is inherited bool bailout = false; for (std::list::const_iterator i = symbolDatabase->scopeList.cbegin(); i != symbolDatabase->scopeList.cend(); ++i) { if (i->definedType) { for (size_t j = 0; j < i->definedType->derivedFrom.size(); j++) { if (i->definedType->derivedFrom[j].type == scope->definedType) { bailout = true; break; } } } } if (bailout) continue; // bail out for extern/global struct for (size_t i = 0; i < symbolDatabase->getVariableListSize(); i++) { const Variable* var = symbolDatabase->getVariableFromVarId(i); 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->classEnd, castPattern.c_str())) continue; // (struct S){..} const std::string initPattern("( struct| " + scope->className + " ) {"); if (Token::findmatch(scope->classEnd, initPattern.c_str())) continue; // Bail out if struct is used in sizeof.. for (const Token *tok = scope->classEnd; 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->classEnd, (scope->className + " %type%| *").c_str())) continue; for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { // 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(_tokenizer->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 char* prefix = isUnion ? "union member '" : "struct member '"; reportError(tok, Severity::style, "unusedStructMember", std::string(prefix) + structname + "::" + varname + "' 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=isRecordTypeWithoutSideEffectsMap.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::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=isEmptyTypeMap.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.82/lib/checkunusedvar.h000066400000000000000000000102231322667425100171630ustar00rootroot00000000000000/* * 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 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) { CheckUnusedVar checkUnusedVar(tokenizer, settings, errorLogger); // Coding style checks checkUnusedVar.checkStructMemberUsage(); checkUnusedVar.checkFunctionVariableUsage(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { (void)tokenizer; (void)settings; (void)errorLogger; } /** @brief %Check for unused function variables */ void checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables, bool insideLoop); 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 { 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 { 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 isRecordTypeWithoutSideEffectsMap; std::map isEmptyTypeMap; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedvarH cppcheck-1.82/lib/checkvaarg.cpp000066400000000000000000000170471322667425100166150ustar00rootroot00000000000000/* * 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 "checkvaarg.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 = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); const bool printWarnings = _settings->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->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (!tok->scope()->isExecutable()) tok = tok->scope()->classEnd; 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 = _tokenizer->getSymbolDatabase(); for (unsigned int varid = 1; varid < symbolDatabase->getVariableListSize(); varid++) { const Variable* var = symbolDatabase->getVariableFromVarId(varid); 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()->classEnd; tok = tok->next()) { 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->classEnd; if (!tok) return; } else if (tok->str() == "goto" || (_tokenizer->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.82/lib/checkvaarg.h000066400000000000000000000061751322667425100162620ustar00rootroot00000000000000/* * 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 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) { } virtual void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { 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 { 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 { 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.82/lib/config.h000066400000000000000000000007611322667425100154240ustar00rootroot00000000000000#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 #include static const std::string emptyString; #endif // configH cppcheck-1.82/lib/cppcheck.cpp000066400000000000000000001017731322667425100162770ustar00rootroot00000000000000/* * 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 "cppcheck.h" #include "check.h" #include "checkunusedfunctions.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 #include #include #include #include #include #include #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 CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions) : _errorLogger(errorLogger), exitcode(0), _useGlobalSuppressions(useGlobalSuppressions), tooManyConfigs(false), _simplify(true) { } CppCheck::~CppCheck() { while (!fileInfo.empty()) { delete fileInfo.back(); fileInfo.pop_back(); } S_timerResults.ShowResults(_settings.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.c_str()); return processFile(Path::simplifyPath(path), emptyString, fin); } unsigned int CppCheck::check(const std::string &path, const std::string &content) { std::istringstream iss(content); return processFile(Path::simplifyPath(path), emptyString, iss); } unsigned int CppCheck::check(const ImportProject::FileSettings &fs) { CppCheck temp(_errorLogger, _useGlobalSuppressions); temp._settings = _settings; if (!temp._settings.userDefines.empty()) temp._settings.userDefines += ';'; temp._settings.userDefines += fs.cppcheckDefines(); temp._settings.includePaths = fs.includePaths; // TODO: temp._settings.userUndefs = fs.undefs; if (fs.platformType != Settings::Unspecified) { temp._settings.platform(fs.platformType); } std::ifstream fin(fs.filename.c_str()); return temp.processFile(Path::simplifyPath(fs.filename), fs.cfg, fin); } unsigned int CppCheck::processFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream) { exitcode = 0; // only show debug warnings for accepted C/C++ source files if (!Path::acceptFile(filename)) _settings.debugwarnings = false; if (_settings.terminated()) return exitcode; if (!_settings.quiet) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); _errorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("...")); if (_settings.verbose) { _errorLogger.reportOut("Defines: " + _settings.userDefines); std::string includePaths; for (std::list::const_iterator I = _settings.includePaths.begin(); I != _settings.includePaths.end(); ++I) includePaths += " -I" + *I; _errorLogger.reportOut("Includes:" + includePaths); _errorLogger.reportOut(std::string("Platform:") + _settings.platformString()); } } if (plistFile.is_open()) { plistFile << ErrorLogger::plistFooter(); plistFile.close(); } CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr); bool internalErrorFound(false); try { Preprocessor preprocessor(_settings, 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 (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) { bool err; 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: 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(it->location.file(), it->location.line); std::list callstack; callstack.push_back(loc1); ErrorLogger::ErrorMessage errmsg(callstack, "", Severity::error, it->msg, "syntaxError", false); _errorLogger.reportErr(errmsg); return 1; } } preprocessor.loadFiles(tokens1, files); if (!_settings.plistOutput.empty()) { std::string filename2; if (filename.find('/') != std::string::npos) filename2 = filename.substr(filename.rfind('/') + 1); else filename2 = filename; filename2 = _settings.plistOutput + filename2.substr(0, filename2.find('.')) + ".plist"; plistFile.open(filename2); plistFile << ErrorLogger::plistHeader(version(), files); } // write dump file xml prolog std::ofstream fdump; if (_settings.dump) { const std::string dumpfile(_settings.dumpFile.empty() ? (filename + ".dump") : _settings.dumpFile); fdump.open(dumpfile.c_str()); 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 << "\" " << "str=\"" << ErrorLogger::toxml(tok->str) << "\"" << "/>" << std::endl; } fdump << " " << std::endl; } } // Parse comments and then remove them preprocessor.inlineSuppressions(tokens1); tokens1.removeComments(); preprocessor.removeComments(); if (!_settings.buildDir.empty()) { // Get toolinfo std::string toolinfo; toolinfo += CPPCHECK_VERSION_STRING; toolinfo += _settings.isEnabled(Settings::WARNING) ? 'w' : ' '; toolinfo += _settings.isEnabled(Settings::STYLE) ? 's' : ' '; toolinfo += _settings.isEnabled(Settings::PERFORMANCE) ? 'p' : ' '; toolinfo += _settings.isEnabled(Settings::PORTABILITY) ? 'p' : ' '; toolinfo += _settings.isEnabled(Settings::INFORMATION) ? 'i' : ' '; toolinfo += _settings.userDefines; // Calculate checksum so it can be compared with old checksum / future checksums const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo); std::list errors; if (!analyzerInformation.analyzeFile(_settings.buildDir, filename, cfgname, checksum, &errors)) { while (!errors.empty()) { reportErr(errors.front()); errors.pop_front(); } return exitcode; // known results => no need to reanalyze file } } // Get directives preprocessor.setDirectives(tokens1); preprocessor.simplifyPragmaAsm(&tokens1); preprocessor.setPlatformInfo(&tokens1); // Get configurations.. if (_settings.userDefines.empty() || _settings.force) { Timer t("Preprocessor::getConfigs", _settings.showtime, &S_timerResults); configurations = preprocessor.getConfigs(tokens1); } else { configurations.insert(_settings.userDefines); } if (_settings.checkConfiguration) { for (std::set::const_iterator it = configurations.begin(); it != configurations.end(); ++it) (void)preprocessor.getcode(tokens1, *it, files, true); return 0; } // Run define rules on raw code for (std::list::const_iterator it = _settings.rules.begin(); it != _settings.rules.end(); ++it) { if (it->tokenlist != "define") continue; std::string code; const std::list &directives = preprocessor.getDirectives(); for (std::list::const_iterator dir = directives.begin(); dir != directives.end(); ++dir) { if (dir->str.compare(0,8,"#define ") == 0) code += "#line " + MathLib::toString(dir->linenr) + " \"" + dir->file + "\"\n" + dir->str + '\n'; } Tokenizer tokenizer2(&_settings, this); std::istringstream istr2(code); tokenizer2.list.createTokens(istr2); executeRules("define", tokenizer2); break; } if (!_settings.force && configurations.size() > _settings.maxConfigs) { if (_settings.isEnabled(Settings::INFORMATION)) { tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size()); } else { tooManyConfigs = true; } } std::set checksums; unsigned int checkCount = 0; for (std::set::const_iterator it = configurations.begin(); it != configurations.end(); ++it) { // bail out if terminated if (_settings.terminated()) break; // Check only a few configurations (default 12), after that bail out, unless --force // was used. if (!_settings.force && ++checkCount > _settings.maxConfigs) break; cfg = *it; // If only errors are printed, print filename after the check if (!_settings.quiet && (!cfg.empty() || it != configurations.begin())) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); _errorLogger.reportOut("Checking " + fixedpath + ": " + cfg + "..."); } if (!_settings.userDefines.empty()) { if (!cfg.empty()) cfg = ";" + cfg; cfg = _settings.userDefines + cfg; } if (_settings.preprocessOnly) { Timer t("Preprocessor::getcode", _settings.showtime, &S_timerResults); std::string codeWithoutCfg = preprocessor.getcode(tokens1, cfg, files, true); t.Stop(); if (codeWithoutCfg.compare(0,5,"#file") == 0) codeWithoutCfg.insert(0U, "//"); std::string::size_type pos = 0; while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos) codeWithoutCfg.insert(pos+1U, "//"); pos = 0; while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos) codeWithoutCfg.insert(pos+1U, "//"); pos = 0; while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos) codeWithoutCfg[pos] = ' '; reportOut(codeWithoutCfg); continue; } Tokenizer _tokenizer(&_settings, this); if (_settings.showtime != SHOWTIME_NONE) _tokenizer.setTimerResults(&S_timerResults); try { bool result; // Create tokens, skip rest of iteration if failed Timer timer("Tokenizer::createTokens", _settings.showtime, &S_timerResults); const simplecpp::TokenList &tokensP = preprocessor.preprocess(tokens1, cfg, files); _tokenizer.createTokens(&tokensP); timer.Stop(); if (tokensP.empty()) continue; // skip rest of iteration if just checking configuration if (_settings.checkConfiguration) continue; // Check raw tokens checkRawTokens(_tokenizer); // Simplify tokens into normal form, skip rest of iteration if failed Timer timer2("Tokenizer::simplifyTokens1", _settings.showtime, &S_timerResults); result = _tokenizer.simplifyTokens1(cfg); timer2.Stop(); if (!result) continue; // dump xml if --dump if (_settings.dump && fdump.is_open()) { fdump << "" << std::endl; preprocessor.dump(fdump); _tokenizer.dump(fdump); fdump << "" << std::endl; } // Skip if we already met the same simplified token list if (_settings.force || _settings.maxConfigs > 1) { const unsigned long long checksum = _tokenizer.list.calculateChecksum(); if (checksums.find(checksum) != checksums.end()) { if (_settings.isEnabled(Settings::INFORMATION) && (_settings.debug || _settings.verbose)) purgedConfigurationMessage(filename, cfg); continue; } checksums.insert(checksum); } // Check normal tokens checkNormalTokens(_tokenizer); // Analyze info.. if (!_settings.buildDir.empty()) checkUnusedFunctions.parseTokens(_tokenizer, filename.c_str(), &_settings, false); // simplify more if required, skip rest of iteration if failed if (_simplify) { // if further simplification fails then skip rest of iteration Timer timer3("Tokenizer::simplifyTokenList2", _settings.showtime, &S_timerResults); result = _tokenizer.simplifyTokenList2(); timer3.Stop(); if (!result) continue; // Check simplified tokens checkSimplifiedTokens(_tokenizer); } } catch (const InternalError &e) { internalErrorFound=true; std::list locationList; ErrorLogger::ErrorMessage::FileLocation loc; if (e.token) { loc.line = e.token->linenr(); const std::string fixedpath = Path::toNativeSeparators(_tokenizer.list.file(e.token)); loc.setfile(fixedpath); } else { ErrorLogger::ErrorMessage::FileLocation loc2; loc2.setfile(Path::toNativeSeparators(filename)); locationList.push_back(loc2); loc.setfile(_tokenizer.list.getSourceFilePath()); } locationList.push_back(loc); ErrorLogger::ErrorMessage errmsg(locationList, _tokenizer.list.getSourceFilePath(), Severity::error, e.errorMessage, e.id, false); reportErr(errmsg); } } // dumped all configs, close root element now if (_settings.dump && fdump.is_open()) fdump << "" << std::endl; } 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); exitcode=1; // e.g. reflect a syntax error } analyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo()); analyzerInformation.close(); // In jointSuppressionReport mode, unmatched suppressions are // collected after all files are processed if (!_settings.jointSuppressionReport && (_settings.isEnabled(Settings::INFORMATION) || _settings.checkConfiguration)) { reportUnmatchedSuppressions(_settings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled())); } _errorList.clear(); if (internalErrorFound && (exitcode==0)) { exitcode=1; } return exitcode; } 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 (_settings.isEnabled(Settings::INFORMATION)) { const ErrorLogger::ErrorMessage::FileLocation loc1(filename, 0); std::list callstack; callstack.push_back(loc1); ErrorLogger::ErrorMessage errmsg(callstack, emptyString, Severity::information, fullmsg, "internalError", false); _errorLogger.reportErr(errmsg); } else { // Report on stdout _errorLogger.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 (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { if (_settings.terminated()) return; if (tokenizer.isMaxTime()) return; Timer timerRunChecks((*it)->name() + "::runChecks", _settings.showtime, &S_timerResults); (*it)->runChecks(&tokenizer, &_settings, this); } // Analyse the tokens.. for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { Check::FileInfo *fi = (*it)->getFileInfo(&tokenizer, &_settings); if (fi != nullptr) { fileInfo.push_back(fi); analyzerInformation.setFileInfo((*it)->name(), fi->toString()); } } executeRules("normal", tokenizer); } //--------------------------------------------------------------------------- // CppCheck - A function that checks a simplified token list //--------------------------------------------------------------------------- void CppCheck::checkSimplifiedTokens(const Tokenizer &tokenizer) { // call all "runSimplifiedChecks" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { if (_settings.terminated()) return; if (tokenizer.isMaxTime()) return; Timer timerSimpleChecks((*it)->name() + "::runSimplifiedChecks", _settings.showtime, &S_timerResults); (*it)->runSimplifiedChecks(&tokenizer, &_settings, this); timerSimpleChecks.Stop(); } if (!_settings.terminated()) executeRules("simple", tokenizer); } void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer) { (void)tokenlist; (void)tokenizer; #ifdef HAVE_RULES // Are there rules to execute? bool isrule = false; for (std::list::const_iterator it = _settings.rules.begin(); it != _settings.rules.end(); ++it) { if (it->tokenlist == tokenlist) isrule = true; } // There is no rule to execute if (isrule == false) 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 (std::list::const_iterator it = _settings.rules.begin(); it != _settings.rules.end(); ++it) { const Settings::Rule &rule = *it; if (rule.pattern.empty() || rule.id.empty() || rule.severity == Severity::none || rule.tokenlist != tokenlist) continue; const char *error = nullptr; int erroffset = 0; pcre *re = pcre_compile(rule.pattern.c_str(),0,&error,&erroffset,nullptr); if (!re) { if (error) { ErrorLogger::ErrorMessage errmsg(std::list(), emptyString, Severity::error, error, "pcre_compile", false); reportErr(errmsg); } continue; } int pos = 0; int ovector[30]= {0}; while (pos < (int)str.size() && 0 <= pcre_exec(re, nullptr, str.c_str(), (int)str.size(), pos, 0, ovector, 30)) { 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); } #endif } Settings &CppCheck::settings() { return _settings; } void CppCheck::tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations) { if (!_settings.isEnabled(Settings::INFORMATION) && !tooManyConfigs) return; tooManyConfigs = false; if (_settings.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 " << _settings.maxConfigs; if (numberOfConfigurations > _settings.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) { tooManyConfigs = false; if (_settings.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) { if (!_settings.library.reportErrors(msg.file0)) return; const std::string errmsg = msg.toString(_settings.verbose); if (errmsg.empty()) return; // Alert only about unique errors if (std::find(_errorList.begin(), _errorList.end(), errmsg) != _errorList.end()) return; std::string file; unsigned int line(0); if (!msg._callStack.empty()) { file = msg._callStack.back().getfile(false); line = msg._callStack.back().line; } if (_useGlobalSuppressions) { if (_settings.nomsg.isSuppressed(msg._id, file, line)) return; } else { if (_settings.nomsg.isSuppressedLocal(msg._id, file, line)) return; } if (!_settings.nofail.isSuppressed(msg._id, file, line) && !_settings.nomsg.isSuppressed(msg._id, file, line)) exitcode = 1; _errorList.push_back(errmsg); _errorLogger.reportErr(msg); analyzerInformation.reportErr(msg, _settings.verbose); if (!_settings.plistOutput.empty() && plistFile.is_open()) { plistFile << ErrorLogger::plistData(msg); } } void CppCheck::reportOut(const std::string &outmsg) { _errorLogger.reportOut(outmsg); } void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value) { _errorLogger.reportProgress(filename, stage, value); } void CppCheck::reportInfo(const ErrorLogger::ErrorMessage &msg) { // Suppressing info message? std::string file; unsigned int line(0); if (!msg._callStack.empty()) { file = msg._callStack.back().getfile(false); line = msg._callStack.back().line; } if (_useGlobalSuppressions) { if (_settings.nomsg.isSuppressed(msg._id, file, line)) return; } else { if (_settings.nomsg.isSuppressedLocal(msg._id, file, line)) return; } _errorLogger.reportInfo(msg); } void CppCheck::reportStatus(unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/) { } void CppCheck::getErrorMessages() { Settings s(_settings); s.addEnabled("warning"); s.addEnabled("style"); s.addEnabled("portability"); s.addEnabled("performance"); s.addEnabled("information"); tooManyConfigs = 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; // Analyse the tokens for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) errors |= (*it)->analyseWholeProgram(fileInfo, _settings, *this); return errors; } void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map &files) { (void)files; if (buildDir.empty()) return; if (_settings.isEnabled(Settings::UNUSED_FUNCTION)) CheckUnusedFunctions::analyseWholeProgram(this, buildDir); std::list fileInfoList; // Load all analyzer info data.. 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 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; 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; for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { if (checkClassAttr == (*it)->name()) fileInfoList.push_back((*it)->loadFileInfoFromXml(e)); } } } // Analyse the tokens for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) (*it)->analyseWholeProgram(fileInfoList, _settings, *this); for (std::list::iterator fi = fileInfoList.begin(); fi != fileInfoList.end(); ++fi) delete (*fi); } bool CppCheck::isUnusedFunctionCheckEnabled() const { return (_settings.jobs == 1 && _settings.isEnabled(Settings::UNUSED_FUNCTION)); } cppcheck-1.82/lib/cppcheck.h000066400000000000000000000161061322667425100157370ustar00rootroot00000000000000/* * 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 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. */ virtual ~CppCheck(); /** * @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() { _simplify = 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: /** @brief There has been an internal error => Report information message */ void internalError(const std::string &filename, const std::string &msg); /** * @brief Process one file. * @param filename file name * @param cfgname cfg name * @param fileStream stream the file content can be read from * @return amount of errors found */ unsigned int processFile(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 Check simplified tokens * @param tokenizer tokenizer instance */ void checkSimplifiedTokens(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" */ virtual void reportErr(const ErrorLogger::ErrorMessage &msg); /** * @brief Information about progress is directed here. * * @param outmsg Message to show, e.g. "Checking main.cpp..." */ virtual void reportOut(const std::string &outmsg); std::list _errorList; Settings _settings; void reportProgress(const std::string &filename, const char stage[], const std::size_t value); /** * Output information messages. */ virtual void reportInfo(const ErrorLogger::ErrorMessage &msg); ErrorLogger &_errorLogger; /** @brief Current preprocessor configuration */ std::string cfg; unsigned int exitcode; bool _useGlobalSuppressions; /** Are there too many configs? */ bool tooManyConfigs; /** Simplify code? true by default */ bool _simplify; /** File info used for whole program analysis */ std::list fileInfo; AnalyzerInformation analyzerInformation; }; /// @} //--------------------------------------------------------------------------- #endif // cppcheckH cppcheck-1.82/lib/cppcheck.natvis000066400000000000000000000037111322667425100170120ustar00rootroot00000000000000 {_str} {_front->_str} - {_back->_str} _files pCurr pCurr = pCurr->_next {_name->_str} {num} ? {type}: {className} {platformType} {files[fileIndex]} : {line} [col={col}] {intvalue} (INT) {tokvalue} (TOK) {floatValue} (FLOAT) [UNKNOWN] {name} = {value} (value_known=true) {name} = ? cppcheck-1.82/lib/cppcheck.vcxproj000066400000000000000000000760771322667425100172200ustar00rootroot00000000000000 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 8.1 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 DynamicLibrary Unicode false v141 ..\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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /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 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y cppcheck-1.82/lib/cppcheck.vcxproj.filters000066400000000000000000000243531322667425100206550ustar00rootroot00000000000000 {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 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.82/lib/cxx11emu.h000066400000000000000000000045601322667425100156330ustar00rootroot00000000000000/* * 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 cxx11emuH #define cxx11emuH //--------------------------------------------------------------------------- /* Emulate certain features of C++11 in a C++98-compatible way. */ #ifdef __cplusplus #if (__GNUC__ <= 4 && __GNUC_MINOR__ < 6 && !defined(__clang__)) || (!defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L) // Null pointer literal // Source: SC22/WG21/N2431 = J16/07-0301 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf const // this is a const object... class { public: template // convertible to any type operator T*() const { // of null non-member return 0; // pointer... } template // or any type of null operator T C::*() const { // member pointer... return 0; } private: void operator&() const; // whose address can't be taken } cppcheck_nullptr_impl = {}; // and whose name is nullptr // An evil workaround for the inability to disable -Wc++0x-compat using a #pragma. // Once -std=c++0x is embraced, the above class can be renamed to nullptr and // the define can be removed. #define nullptr cppcheck_nullptr_impl // Static assertions #ifndef static_assert #define XXJOIN(x, y) XXJOIN_AGAIN(x, y) #define XXJOIN_AGAIN(x, y) x ## y #define static_assert(e, msg) \ typedef char XXJOIN(assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1] #endif // static_assert #endif // __cplusplus < 201103L #endif // __cplusplus //--------------------------------------------------------------------------- #endif // cxx11emuH cppcheck-1.82/lib/errorlogger.cpp000066400000000000000000000606001322667425100170410ustar00rootroot00000000000000/* * 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 "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) { switch (type) { case AST: id = "internalAstError"; break; case SYNTAX: id = "syntaxError"; break; case INTERNAL: id = "cppcheckError"; 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.push_back(ErrorLogger::ErrorMessage::FileLocation(*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.push_back(ErrorLogger::ErrorMessage::FileLocation(*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.push_back(ErrorLogger::ErrorMessage::FileLocation(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"); _shortMessage = attr ? attr : ""; attr = errmsg->Attribute("verbose"); _verboseMessage = 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 *file = strfile ? strfile : unknown; const char *info = strinfo ? strinfo : ""; const int line = strline ? std::atoi(strline) : 0; _callStack.push_back(ErrorLogger::ErrorMessage::FileLocation(file, info, line)); } } } 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 _verboseMessage 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 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'); if (pos == std::string::npos) { _shortMessage = msg; _verboseMessage = msg; } else { _shortMessage = msg.substr(0, pos); _verboseMessage = msg.substr(pos + 1); } } 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(_shortMessage); const std::string saneVerboseMessage = fixInvalidChars(_verboseMessage); 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 << ':' << (*loc).getfile() << '\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) { 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; _shortMessage = results[3]; _verboseMessage = 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); } const std::string::size_type colonPos = temp.find(':'); if (colonPos == std::string::npos) throw InternalError(nullptr, "Internal Error: No colon found in pattern"); const std::string::size_type tabPos = temp.find('\t'); if (tabPos == std::string::npos) throw InternalError(nullptr, "Internal Error: No tab found in pattern"); const std::string tempinfo = temp.substr(tabPos + 1); temp.erase(tabPos); const std::string tempfile = temp.substr(colonPos + 1); temp.erase(colonPos); const std::string templine = temp; ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(tempfile); loc.setinfo(tempinfo); std::istringstream fiss(templine); fiss >> loc.line; _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(_shortMessage).c_str()); printer.PushAttribute("verbose", fixInvalidChars(_verboseMessage).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", (*it).line); if (!it->getinfo().empty()) printer.PushAttribute("info", it->getinfo().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(); } } std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string &outputFormat) const { // Save this ErrorMessage in plain text. // No template is given if (outputFormat.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 ? _verboseMessage : _shortMessage); return text.str(); } else if (outputFormat == "daca2") { // This is a clang-like output format for daca2 std::ostringstream text; if (_callStack.empty()) { text << "nofile:0:0: "; } else { const ErrorLogger::ErrorMessage::FileLocation &loc = _callStack.back(); text << loc.getfile() << ':' << loc.line << ':' << loc.col << ": "; } if (_inconclusive) text << "inconclusive "; text << Severity::toString(_severity) << ": "; text << (verbose ? _verboseMessage : _shortMessage) << " [" << _id << ']'; if (_callStack.size() <= 1U) return text.str(); for (std::list::const_iterator loc = _callStack.begin(); loc != _callStack.end(); ++loc) text << std::endl << loc->getfile() << ':' << loc->line << ':' << loc->col << ": note: " << (loc->getinfo().empty() ? _shortMessage : loc->getinfo()); return text.str(); } // template is given. Reformat the output according to it else { std::string result = outputFormat; // 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); findAndReplace(result, "{severity}", Severity::toString(_severity)); findAndReplace(result, "{message}", verbose ? _verboseMessage : _shortMessage); findAndReplace(result, "{callstack}", _callStack.empty() ? emptyString : callStackToString(_callStack)); if (!_callStack.empty()) { std::ostringstream oss; oss << _callStack.back().line; findAndReplace(result, "{line}", oss.str()); findAndReplace(result, "{file}", _callStack.back().getfile()); } else { findAndReplace(result, "{file}", emptyString); findAndReplace(result, "{line}", emptyString); } return result; } } void ErrorLogger::reportUnmatchedSuppressions(const std::list &unmatched) { // Report unmatched suppressions for (std::list::const_iterator i = unmatched.begin(); i != unmatched.end(); ++i) { // don't report "unmatchedSuppression" as unmatched if (i->id == "unmatchedSuppression") continue; // check if this unmatched suppression is suppressed bool suppressed = false; for (std::list::const_iterator i2 = unmatched.begin(); i2 != unmatched.end(); ++i2) { if (i2->id == "unmatchedSuppression") { if ((i2->file == "*" || i2->file == i->file) && (i2->line == 0 || i2->line == i->line)) { suppressed = true; break; } } } if (suppressed) continue; const std::list callStack = make_container< std::list > () << ErrorLogger::ErrorMessage::FileLocation(i->file, i->line); reportErr(ErrorLogger::ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + i->id, "unmatchedSuppression", false)); } } 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()), col(tok->col()), _file(tokenList->file(tok)) { } ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList) : fileIndex(tok->fileIndex()), line(tok->linenr()), col(tok->col()), _file(tokenList->file(tok)), _info(info) { } std::string ErrorLogger::ErrorMessage::FileLocation::getfile(bool convert) const { if (convert) return Path::toNativeSeparators(_file); return _file; } void ErrorLogger::ErrorMessage::FileLocation::setfile(const std::string &file) { _file = file; _file = Path::fromNativeSeparators(_file); _file = Path::simplifyPath(_file); } std::string ErrorLogger::ErrorMessage::FileLocation::stringify() const { std::ostringstream oss; oss << '[' << Path::toNativeSeparators(_file); if (line != 0) oss << ':' << line; oss << ']'; return oss.str(); } std::string ErrorLogger::toxml(const std::string &str) { std::ostringstream xml; const bool isstring(str[0] == '\"'); for (std::size_t i = 0U; i < str.length(); i++) { char c = str[i]; switch (c) { case '<': xml << "<"; break; case '>': xml << ">"; break; case '&': xml << "&"; break; case '\"': xml << """; break; case '\0': xml << "\\0"; break; default: if (!isstring || (c >= ' ' && c <= 'z')) 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 (unsigned int i = 0; i < files.size(); ++i) ostr << " " << ErrorLogger::toxml(files[i]) << "\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.col << "\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" << " " << 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(); } cppcheck-1.82/lib/errorlogger.h000066400000000000000000000302731322667425100165110ustar00rootroot00000000000000/* * 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 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 ID) : id(ID) {} 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, INTERNAL}; InternalError(const Token *tok, const std::string &errorMsg, Type type = INTERNAL); const Token *token; std::string errorMessage; 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), col(0) { } FileLocation(const std::string &file, unsigned int aline) : fileIndex(0), line(aline), col(0), _file(file) { } FileLocation(const std::string &file, const std::string &info, unsigned int aline) : fileIndex(0), line(aline), col(0), _file(file), _info(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; /** * 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; unsigned int line; unsigned int col; std::string getinfo() const { return _info; } void setinfo(const std::string &i) { _info = i; } private: std::string _file; std::string _info; }; 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 outputFormat Empty string to use default output format * or template to be used. E.g. "{file}:{line},{severity},{id},{message}" * @return formatted string */ std::string toString(bool verbose, const std::string &outputFormat = 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 _shortMessage; } /** Verbose message (may be the same as the short message) */ const std::string &verboseMessage() const { return _verboseMessage; } 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 _shortMessage; /** Verbose message */ std::string _verboseMessage; }; 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 list of unmatched suppressions * @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions) */ void 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" ""; } }; /// @} //--------------------------------------------------------------------------- #endif // errorloggerH cppcheck-1.82/lib/importproject.cpp000066400000000000000000000565501322667425100174220ustar00rootroot00000000000000/* * 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 "importproject.h" #include "path.h" #include "settings.h" #include "tinyxml2.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #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::size_t i = 0; i < ipaths.size(); ++i) { if (it->filename.size() > ipaths[i].size() && it->filename.compare(0,ipaths[i].size(),ipaths[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) { std::string::size_type pos1 = defs.find(";%("); 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); if (!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) { 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 I; // only parse each includePath once - so remove duplicates std::list uniqueIncludePaths = in; uniqueIncludePaths.sort(); uniqueIncludePaths.unique(); for (std::list::const_iterator it = uniqueIncludePaths.begin(); it != uniqueIncludePaths.end(); ++it) { 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 += '/'; I.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; I.push_back(s + '/'); } includePaths.swap(I); } void ImportProject::import(const std::string &filename) { std::ifstream fin(filename); if (!fin.is_open()) return; if (filename.find("compile_commands.json") != std::string::npos) { importCompileCommands(fin); } else if (filename.find(".sln") != std::string::npos) { std::string path(Path::getPathFromFilename(Path::fromNativeSeparators(filename))); if (!path.empty() && !endsWith(path,'/')) path += '/'; importSln(fin,path); } else if (filename.find(".vcxproj") != std::string::npos) { std::map variables; importVcxproj(filename, variables, emptyString); } } void ImportProject::importCompileCommands(std::istream &istr) { std::map values; // TODO: Use a JSON parser Settings settings; TokenList tokenList(&settings); tokenList.createTokens(istr); for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%str% : %str% [,}]")) { const std::string& key = tok->str(); const std::string& value = tok->strAt(2); values[key.substr(1, key.size() - 2U)] = value.substr(1, value.size() - 2U); } else if (tok->str() == "}") { if (!values["file"].empty() && !values["command"].empty()) { struct FileSettings fs; fs.filename = Path::fromNativeSeparators(values["file"]); const std::string& command = values["command"]; const std::string directory = Path::fromNativeSeparators(values["directory"]); std::string::size_type pos = 0; while (std::string::npos != (pos = command.find(' ',pos))) { pos++; if (pos >= command.size()) break; if (command[pos] != '/' && command[pos] != '-') continue; pos++; if (pos >= command.size()) break; char F = command[pos++]; std::string fval; while (pos < command.size() && command[pos] != ' ' && command[pos] != '=') { if (command[pos] != '\\') fval += command[pos]; pos++; } if (F=='D') { std::string defval; bool escape = false; while (pos < command.size() && command[pos] != ' ') { if (command[pos] != '\\') { defval += command[pos]; escape = false; } else { if (escape) { defval += '\\'; escape = false; } else { escape = true; } } pos++; } fs.defines += fval; if (!defval.empty()) fs.defines += defval; fs.defines += ';'; } else if (F=='U') fs.undefs.insert(fval); else if (F=='I') fs.includePaths.push_back(fval); else if (F=='s' && fval.compare(0,3,"td=") == 0) fs.standard = fval.substr(3); else if (F == 'i' && fval == "system") { ++pos; std::string isystem; while (pos < command.size() && command[pos] != ' ') { if (command[pos] != '\\') isystem += command[pos]; pos++; } fs.systemIncludePaths.push_back(isystem); } } std::map variables; fs.setIncludePaths(directory, fs.includePaths, variables); fs.setDefines(fs.defines); fileSettings.push_back(fs); } values.clear(); } } } 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); 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 cant 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.push_back(ItemDefinitionGroup(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; 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) { ProjectConfiguration p(cfg); if (p.platform != ProjectConfiguration::Unknown) projectConfigurationList.push_back(ProjectConfiguration(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.push_back(include); } } } } else if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) { itemDefinitionGroupList.push_back(ItemDefinitionGroup(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 (std::list::const_iterator c = compileList.begin(); c != compileList.end(); ++c) { for (std::list::const_iterator p = projectConfigurationList.begin(); p != projectConfigurationList.end(); ++p) { 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 (std::list::const_iterator i = itemDefinitionGroupList.begin(); i != itemDefinitionGroupList.end(); ++i) { 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); } } } cppcheck-1.82/lib/importproject.h000066400000000000000000000057361322667425100170670ustar00rootroot00000000000000/* * 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 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; } }; } /** * @brief Importing project settings. */ class CPPCHECKLIB ImportProject { public: /** 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 setDefines(std::string defs); void setIncludePaths(const std::string &basepath, const std::list &in, std::map &variables); }; std::list fileSettings; void ignorePaths(const std::vector &ipaths); void ignoreOtherConfigs(const std::string &cfg); void ignoreOtherPlatforms(cppcheck::Platform::PlatformType platformType); void import(const std::string &filename); protected: void importCompileCommands(std::istream &istr); private: void importSln(std::istream &istr, const std::string &path); void importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories); }; /// @} //--------------------------------------------------------------------------- #endif // importprojectH cppcheck-1.82/lib/lib.pri000066400000000000000000000066761322667425100153030ustar00rootroot00000000000000# no manual edits - this file is autogenerated by dmake include($$PWD/pcrerules.pri) include($$PWD/../externals/externals.pri) INCLUDEPATH += $$PWD HEADERS += $${PWD}/check.h \ $${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}/errorlogger.h \ $${PWD}/importproject.h \ $${PWD}/library.h \ $${PWD}/mathlib.h \ $${PWD}/path.h \ $${PWD}/pathmatch.h \ $${PWD}/platform.h \ $${PWD}/preprocessor.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}/errorlogger.cpp \ $${PWD}/importproject.cpp \ $${PWD}/library.cpp \ $${PWD}/mathlib.cpp \ $${PWD}/path.cpp \ $${PWD}/pathmatch.cpp \ $${PWD}/platform.cpp \ $${PWD}/preprocessor.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.82/lib/library.cpp000066400000000000000000001404001322667425100161510ustar00rootroot00000000000000/* * 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 "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.push_back(std::string(names, p-names)); names = p + 1; } ret.push_back(names); return ret; } Library::Library() : allocid(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_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 CFGDIR cfgfolders.push_back(CFGDIR); #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.front()); cfgfolders.pop_front(); 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 (_files.find(absolute_path) == _files.end()) { Error err = load(doc); if (err.errorcode == OK) _files.insert(absolute_path); return err; } return Error(OK); // ignore duplicates } return Error(error == tinyxml2::XML_ERROR_FILE_NOT_FOUND ? FILE_NOT_FOUND : 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) 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 = _dealloc.find(memorynode->GetText()); if (it != _dealloc.end()) { allocationId = it->second.groupId; break; } } } if (allocationId == 0) { if (nodename == "memory") while (!ismemory(++allocid)); else while (!isresource(++allocid)); allocationId = allocid; } // 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") { AllocFunc temp; 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; _alloc[memorynode->GetText()] = temp; } else if (memorynodename == "dealloc") { AllocFunc temp; temp.groupId = allocationId; const char *arg = memorynode->Attribute("arg"); if (arg) temp.arg = atoi(arg); else temp.arg = 1; _dealloc[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("#define ") + name + " " + value + "\n"); } else if (nodename == "function") { const char *name = node->Attribute("name"); if (name == nullptr) return Error(MISSING_ATTRIBUTE, "name"); const std::vector names(getnames(name)); for (unsigned int i = 0U; i < names.size(); ++i) { const Error &err = loadFunction(node, names[i], 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"); _reflection[reflectionnode->GetText()] = atoi(argString); } } else if (nodename == "markup") { const char * const extension = node->Attribute("ext"); if (!extension) return Error(MISSING_ATTRIBUTE, "ext"); _markupExtensions.insert(extension); _reporterrors[extension] = (node->Attribute("reporterrors", "true") != nullptr); _processAfterCode[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"); _keywords[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") _exporters[prefix].addPrefix(e->GetText()); else if (ename == "suffix") _exporters[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) _importers[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) _executableblocks[extension].addBlock(blockName); } else if (blocknodename == "structure") { const char * start = blocknode->Attribute("start"); if (start) _executableblocks[extension].setStart(start); const char * end = blocknode->Attribute("end"); if (end) _executableblocks[extension].setEnd(end); const char * offset = blocknode->Attribute("offset"); if (offset) _executableblocks[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) { 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; 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"; 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::NO_ACTION; if (action_ptr) { std::string actionName = action_ptr; if (actionName == "resize") action = Container::RESIZE; else if (actionName == "clear") action = Container::CLEAR; else if (actionName == "push") action = Container::PUSH; else if (actionName == "pop") action = Container::POP; else if (actionName == "find") action = Container::FIND; else if (actionName == "insert") action = Container::INSERT; else if (actionName == "erase") action = Container::ERASE; else if (actionName == "change-content") action = Container::CHANGE_CONTENT; else if (actionName == "change-internal") action = Container::CHANGE_INTERNAL; else if (actionName == "change") action = Container::CHANGE; else return Error(BAD_ATTRIBUTE_VALUE, actionName); } const char* const yield_ptr = functionNode->Attribute("yields"); Container::Yield yield = Container::NO_YIELD; if (yield_ptr) { std::string yieldName = yield_ptr; if (yieldName == "at_index") yield = Container::AT_INDEX; else if (yieldName == "item") yield = Container::ITEM; else if (yieldName == "buffer") yield = Container::BUFFER; else if (yieldName == "buffer-nt") yield = Container::BUFFER_NT; else if (yieldName == "start-iterator") yield = Container::START_ITERATOR; else if (yieldName == "end-iterator") yield = Container::END_ITERATOR; else if (yieldName == "iterator") yield = Container::ITERATOR; else if (yieldName == "size") yield = Container::SIZE; else if (yieldName == "empty") yield = Container::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"; } else unknown_elements.insert(containerNodeName); } } else if (nodename == "podtype") { const char * const name = node->Attribute("name"); if (!name) return Error(MISSING_ATTRIBUTE, "name"); PodType podType = {0}; 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; const std::vector names(getnames(name)); for (unsigned int i = 0U; i < names.size(); ++i) podtypes[names[i]] = 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._type = 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._signed = true; else if (typenodename == "unsigned") type._unsigned = true; else if (typenodename == "long") type._long = true; else if (typenodename == "pointer") type._pointer= true; else if (typenodename == "ptr_ptr") type._ptr_ptr = true; else if (typenodename == "const_ptr") type._const_ptr = 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); } platform_types[type_name] = type; } else { std::set::const_iterator it; for (it = platform.begin(); it != platform.end(); ++it) { const PlatformType * const type_ptr = platform_type(type_name, *it); if (type_ptr) { if (*type_ptr == type) return Error(DUPLICATE_PLATFORM_TYPE, type_name); return Error(PLATFORM_TYPE_REDEFINED, type_name); } platforms[*it]._platform_types[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") _noreturn[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()) _returnValue[name] = expr; if (const char *type = functionnode->Attribute("type")) _returnValueType[name] = type; if (const char *container = functionnode->Attribute("container")) _returnValueContainer[name] = std::atoi(container); } 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; for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { const std::string argnodename = argnode->Name(); if (argnodename == "not-bool") ac.notbool = true; else if (argnodename == "not-null") ac.notnull = true; else if (argnodename == "not-uninit") ac.notuninit = true; 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; for (; *p; p++) { if (std::isdigit(*p)) error |= (*(p+1) == '-'); else if (*p == ':') error |= range; else if (*p == '-') error |= (!std::isdigit(*(p+1))); else if (*p == ',') range = false; else error = true; range |= (*p == ':'); } 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::STRLEN; else if (strcmp(typeattr,"argvalue")==0) type = ArgumentChecks::MinSize::ARGVALUE; else if (strcmp(typeattr,"sizeof")==0) type = ArgumentChecks::MinSize::SIZEOF; else if (strcmp(typeattr,"mul")==0) type = ArgumentChecks::MinSize::MUL; else return Error(BAD_ATTRIBUTE_VALUE, typeattr); 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::MUL ? 2 : 1); ac.minsizes.push_back(ArgumentChecks::MinSize(type,argattr[0]-'0')); if (type == ArgumentChecks::MinSize::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); } } 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 wi.message = functionnode->GetText(); functionwarn[name] = wi; } else unknown_elements.insert(functionnodename); } return Error(OK); } bool Library::isargvalid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const { const ArgumentChecks *ac = getarg(ftok, argnr); if (!ac || ac->valid.empty()) return true; TokenList tokenList(nullptr); std::istringstream istr(ac->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(); } } 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; } 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 (unsigned int i = 0; i < derivedFrom.size(); ++i) { const Type::BaseInfo &baseInfo = derivedFrom[i]; 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% (")) return ""; // Lookup function name using AST.. if (ftok->astParent()) { bool error = false; std::string ret = getFunctionName(ftok->next()->astOperand1(), &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); 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) const { const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // non-scan format string argument should not be uninitialized const std::string funcname = getFunctionName(ftok); 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; } /** get allocation info for function */ const Library::AllocFunc* Library::alloc(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(_alloc, funcname); } /** get deallocation info for function */ const Library::AllocFunc* Library::dealloc(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(_dealloc, funcname); } /** get allocation id for function */ int Library::alloc(const Token *tok, int arg) const { const Library::AllocFunc* af = alloc(tok); return (af && af->arg == arg) ? af->groupId : 0; } /** get deallocation id for function */ int Library::dealloc(const Token *tok, int arg) const { const Library::AllocFunc* af = dealloc(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; 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::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.startPattern.c_str())) { 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; } // 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 { 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; 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; 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; std::map::const_iterator it = _returnValue.find(getFunctionName(ftok)); return it != _returnValue.end() ? it->second : emptyString; } const std::string& Library::returnValueType(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return emptyString; std::map::const_iterator it = _returnValueType.find(getFunctionName(ftok)); return it != _returnValueType.end() ? it->second : emptyString; } int Library::returnValueContainer(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return -1; std::map::const_iterator it = _returnValueContainer.find(getFunctionName(ftok)); return it != _returnValueContainer.end() ? it->second : -1; } bool Library::hasminsize(const std::string &functionName) const { std::map::const_iterator it1 = functions.find(functionName); 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; } bool Library::ignorefunction(const std::string& functionName) 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 { 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 { 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 { 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; 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; std::map::const_iterator it = _noreturn.find(getFunctionName(ftok)); return (it != _noreturn.end() && it->second); } bool Library::isnotnoreturn(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeNoreturn()) return false; if (isNotLibraryFunction(ftok)) return false; std::map::const_iterator it = _noreturn.find(getFunctionName(ftok)); return (it != _noreturn.end() && !it->second); } bool Library::markupFile(const std::string &path) const { return _markupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != _markupExtensions.end(); } bool Library::processMarkupAfterCode(const std::string &path) const { const std::map::const_iterator it = _processAfterCode.find(Path::getFilenameExtensionInLowerCase(path)); return (it == _processAfterCode.end() || it->second); } bool Library::reportErrors(const std::string &path) const { const std::map::const_iterator it = _reporterrors.find(Path::getFilenameExtensionInLowerCase(path)); return (it == _reporterrors.end() || it->second); } bool Library::isexecutableblock(const std::string &file, const std::string &token) const { const std::map::const_iterator it = _executableblocks.find(Path::getFilenameExtensionInLowerCase(file)); return (it != _executableblocks.end() && it->second.isBlock(token)); } int Library::blockstartoffset(const std::string &file) const { int offset = -1; const std::map::const_iterator map_it = _executableblocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != _executableblocks.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 = _executableblocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != _executableblocks.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 = _executableblocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != _executableblocks.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 = _keywords.find(Path::getFilenameExtensionInLowerCase(file)); return (it != _keywords.end() && it->second.count(keyword)); } bool Library::isimporter(const std::string& file, const std::string &importer) const { const std::map >::const_iterator it = _importers.find(Path::getFilenameExtensionInLowerCase(file)); return (it != _importers.end() && it->second.count(importer) > 0); } cppcheck-1.82/lib/library.h000066400000000000000000000433261322667425100156270ustar00rootroot00000000000000/* * 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 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; }; /** get allocation info for function */ const AllocFunc* alloc(const Token *tok) const; /** get deallocation info for function */ const AllocFunc* dealloc(const Token *tok) const; /** get allocation id for function */ int alloc(const Token *tok, int arg) const; /** get deallocation id for function */ int dealloc(const Token *tok, int arg) const; /** get allocation info for function by name (deprecated, use other alloc) */ const AllocFunc* alloc(const char name[]) const { return getAllocDealloc(_alloc, name); } /** get deallocation info for function by name (deprecated, use other alloc) */ const AllocFunc* dealloc(const char name[]) const { return getAllocDealloc(_dealloc, name); } /** get allocation id for function by name (deprecated, use other alloc) */ int allocId(const char name[]) const { const AllocFunc* af = getAllocDealloc(_alloc, 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(_dealloc, name); return af ? af->groupId : 0; } /** set allocation id for function */ void setalloc(const std::string &functionname, int id, int arg) { _alloc[functionname].groupId = id; _alloc[functionname].arg = arg; } void setdealloc(const std::string &functionname, int id, int arg) { _dealloc[functionname].groupId = id; _dealloc[functionname].arg = arg; } /** add noreturn function setting */ void setnoreturn(const std::string& funcname, bool noreturn) { _noreturn[funcname] = noreturn; } /** is allocation type memory? */ static bool ismemory(int id) { return ((id > 0) && ((id & 1) == 0)); } static bool ismemory(const AllocFunc* func) { return ((func->groupId > 0) && ((func->groupId & 1) == 0)); } /** is allocation type resource? */ static bool isresource(int id) { return ((id > 0) && ((id & 1) == 1)); } static bool isresource(const AllocFunc* 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; 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), opLessAllowed(true) { } enum Action { RESIZE, CLEAR, PUSH, POP, FIND, INSERT, ERASE, CHANGE_CONTENT, CHANGE, CHANGE_INTERNAL, NO_ACTION }; enum 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, endPattern, itEndPattern; std::map functions; int type_templateArgNo; int size_templateArgNo; bool arrayLike_indexOp; bool stdStringLike; bool opLessAllowed; Action getAction(const std::string& function) const { std::map::const_iterator i = functions.find(function); if (i != functions.end()) return i->second.action; return NO_ACTION; } Yield getYield(const std::string& function) const { std::map::const_iterator i = functions.find(function); if (i != functions.end()) return i->second.yield; return NO_YIELD; } }; std::map containers; const Container* detectContainer(const Token* typeStart, bool iterator = false) const; class ArgumentChecks { public: ArgumentChecks() : notbool(false), notnull(false), notuninit(false), formatstr(false), strz(false), optional(false), variadic(false), iteratorInfo() { } bool notbool; bool notnull; bool 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 }; MinSize(Type t, int a) : type(t), arg(a), arg2(0) {} Type type; int arg; int arg2; }; std::vector minsizes; }; 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) 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 isargvalid(const Token *ftok, int argnr, const MathLib::bigint 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 std::string &functionName) const; const std::vector *argminsizes(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg ? &arg->minsizes : nullptr; } bool markupFile(const std::string &path) const; bool processMarkupAfterCode(const std::string &path) const; const std::set &markupExtensions() const { return _markupExtensions; } 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 _exporters.find(prefix) != _exporters.end(); } bool isexportedprefix(const std::string &prefix, const std::string &token) const { const std::map::const_iterator it = _exporters.find(prefix); return (it != _exporters.end() && it->second.isPrefix(token)); } bool isexportedsuffix(const std::string &prefix, const std::string &token) const { const std::map::const_iterator it = _exporters.find(prefix); return (it != _exporters.end() && it->second.isSuffix(token)); } bool isimporter(const std::string& file, const std::string &importer) const; bool isreflection(const std::string &token) const { return _reflection.find(token) != _reflection.end(); } int reflectionArgument(const std::string &token) const { const std::map::const_iterator it = _reflection.find(token); if (it != _reflection.end()) return it->second; return -1; } std::set returnuninitdata; std::vector defines; // to provide some library defines struct PodType { unsigned int size; char sign; }; const struct PodType *podtype(const std::string &name) const { const std::map::const_iterator it = podtypes.find(name); return (it != podtypes.end()) ? &(it->second) : nullptr; } struct PlatformType { PlatformType() : _signed(false) , _unsigned(false) , _long(false) , _pointer(false) , _ptr_ptr(false) , _const_ptr(false) { } bool operator == (const PlatformType & type) const { return (_signed == type._signed && _unsigned == type._unsigned && _long == type._long && _pointer == type._pointer && _ptr_ptr == type._ptr_ptr && _const_ptr == type._const_ptr && _type == type._type); } bool operator != (const PlatformType & type) const { return !(*this == type); } std::string _type; bool _signed; bool _unsigned; bool _long; bool _pointer; bool _ptr_ptr; bool _const_ptr; }; struct Platform { const PlatformType *platform_type(const std::string &name) const { const std::map::const_iterator it = _platform_types.find(name); return (it != _platform_types.end()) ? &(it->second) : nullptr; } std::map _platform_types; }; const PlatformType *platform_type(const std::string &name, const std::string & platform) const { const std::map::const_iterator it = platforms.find(platform); if (it != platforms.end()) { const PlatformType * const type = it->second.platform_type(name); if (type) return type; } const std::map::const_iterator it2 = platform_types.find(name); return (it2 != platform_types.end()) ? &(it2->second) : nullptr; } /** * Get function name for function call */ std::string getFunctionName(const Token *ftok) 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) { _prefixes.insert(prefix); } void addSuffix(const std::string& suffix) { _suffixes.insert(suffix); } bool isPrefix(const std::string& prefix) const { return (_prefixes.find(prefix) != _prefixes.end()); } bool isSuffix(const std::string& suffix) const { return (_suffixes.find(suffix) != _suffixes.end()); } private: std::set _prefixes; std::set _suffixes; }; class CodeBlock { public: CodeBlock() : _offset(0) {} void setStart(const char* s) { _start = s; } void setEnd(const char* e) { _end = e; } void setOffset(const int o) { _offset = o; } void addBlock(const char* blockName) { _blocks.insert(blockName); } const std::string& start() const { return _start; } const std::string& end() const { return _end; } int offset() const { return _offset; } bool isBlock(const std::string& blockName) const { return _blocks.find(blockName) != _blocks.end(); } private: std::string _start; std::string _end; int _offset; std::set _blocks; }; int allocid; std::set _files; std::map _alloc; // allocation functions std::map _dealloc; // deallocation functions std::map _noreturn; // is function noreturn? std::map _returnValue; std::map _returnValueType; std::map _returnValueContainer; std::map _reporterrors; std::map _processAfterCode; std::set _markupExtensions; // file extensions of markup files std::map > _keywords; // keywords for code in the library std::map _executableblocks; // keywords for blocks of executable code std::map _exporters; // keywords that export variables/functions to libraries (meta-code/macros) std::map > _importers; // keywords that import variables/functions std::map _reflection; // invocation of reflection std::map podtypes; // pod types std::map platform_types; // platform independent typedefs std::map platforms; // platform dependent typedefs 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; } }; /// @} //--------------------------------------------------------------------------- #endif // libraryH cppcheck-1.82/lib/matchcompiler.h000066400000000000000000000036561322667425100170140ustar00rootroot00000000000000/* * 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.82/lib/mathlib.cpp000066400000000000000000001233151322667425100161330ustar00rootroot00000000000000/* * 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 "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) : intValue(0), doubleValue(0), isUnsigned(false) { if (MathLib::isFloat(s)) { type = MathLib::value::FLOAT; doubleValue = MathLib::toDoubleNumber(s); return; } if (!MathLib::isInt(s)) throw InternalError(nullptr, "Invalid value: " + s); type = MathLib::value::INT; intValue = MathLib::toLongNumber(s); if (isIntHex(s) && intValue < 0) isUnsigned = true; // read suffix if (s.size() >= 2U) { for (std::size_t i = s.size() - 1U; i > 0U; --i) { char c = s[i]; if (c == 'u' || c == 'U') isUnsigned = true; else if (c == 'l' || c == 'L') { if (type == MathLib::value::INT) type = MathLib::value::LONG; else if (type == MathLib::value::LONG) type = MathLib::value::LONGLONG; } else if (i > 2U && c == '4' && s[i-1] == '6' && s[i-2] == 'i') type = MathLib::value::LONGLONG; } } } std::string MathLib::value::str() const { std::ostringstream ostr; if (type == MathLib::value::FLOAT) { if (ISNAN(doubleValue)) return "nan.0"; if (ISINF(doubleValue)) return (doubleValue > 0) ? "inf.0" : "-inf.0"; ostr.precision(9); ostr << std::fixed << doubleValue; // 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 (isUnsigned) ostr << static_cast(intValue) << "U"; else ostr << intValue; if (type == MathLib::value::LONG) ostr << "L"; else if (type == MathLib::value::LONGLONG) ostr << "LL"; return ostr.str(); } void MathLib::value::promote(const MathLib::value &v) { if (isInt() && v.isInt()) { if (type < v.type) { type = v.type; isUnsigned = v.isUnsigned; } else if (type == v.type) { isUnsigned |= v.isUnsigned; } } else if (!isFloat()) { isUnsigned = false; doubleValue = intValue; type = 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.doubleValue += v2.getDoubleValue(); break; case '-': temp.doubleValue -= v2.getDoubleValue(); break; case '*': temp.doubleValue *= v2.getDoubleValue(); break; case '/': temp.doubleValue /= v2.getDoubleValue(); break; case '%': case '&': case '|': case '^': throw InternalError(nullptr, "Invalid calculation"); default: throw InternalError(nullptr, "Unhandled calculation"); } } else if (temp.isUnsigned) { switch (op) { case '+': temp.intValue += (unsigned long long)v2.intValue; break; case '-': temp.intValue -= (unsigned long long)v2.intValue; break; case '*': temp.intValue *= (unsigned long long)v2.intValue; break; case '/': if (v2.intValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (v1.intValue == std::numeric_limits::min() && std::abs(v2.intValue)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); temp.intValue /= (unsigned long long)v2.intValue; break; case '%': if (v2.intValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); temp.intValue %= (unsigned long long)v2.intValue; break; case '&': temp.intValue &= (unsigned long long)v2.intValue; break; case '|': temp.intValue |= (unsigned long long)v2.intValue; break; case '^': temp.intValue ^= (unsigned long long)v2.intValue; break; default: throw InternalError(nullptr, "Unhandled calculation"); } } else { switch (op) { case '+': temp.intValue += v2.intValue; break; case '-': temp.intValue -= v2.intValue; break; case '*': temp.intValue *= v2.intValue; break; case '/': if (v2.intValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (v1.intValue == std::numeric_limits::min() && std::abs(v2.intValue)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); temp.intValue /= v2.intValue; break; case '%': if (v2.intValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); temp.intValue %= v2.intValue; break; case '&': temp.intValue &= v2.intValue; break; case '|': temp.intValue |= v2.intValue; break; case '^': temp.intValue ^= v2.intValue; 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.doubleValue < v.getDoubleValue()) return -1; if (temp.doubleValue > v.getDoubleValue()) return 1; return 0; } if (temp.isUnsigned) { if ((unsigned long long)intValue < (unsigned long long)v.intValue) return -1; if ((unsigned long long)intValue > (unsigned long long)v.intValue) return 1; return 0; } if (intValue < v.intValue) return -1; if (intValue > v.intValue) return 1; return 0; } MathLib::value MathLib::value::add(int v) const { MathLib::value temp(*this); if (temp.isInt()) temp.intValue += v; else temp.doubleValue += 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.intValue >= MathLib::bigint_bits) { return ret; } ret.intValue <<= v.intValue; 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.intValue >= MathLib::bigint_bits) { return ret; } ret.intValue >>= v.intValue; 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(str.front()); for (std::string::const_iterator it=str.begin()+1; it!=str.end(); ++it) { 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(), NULL, 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 (str[0] == '\'' && str.size() >= 3U && endsWith(str,'\'')) { return characterLiteralToLongNumber(str.substr(1,str.size()-2)); } 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; } double MathLib::toDoubleNumber(const std::string &str) { if (str[0] == '\'' && str.size() >= 3U && endsWith(str,'\'')) return characterLiteralToLongNumber(str.substr(1,str.size()-2)); 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 // 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 State { START, BASE_PLUSMINUS, BASE_DIGITS1, LEADING_DECIMAL, TRAILING_DECIMAL, BASE_DIGITS2, E, MANTISSA_PLUSMINUS, MANTISSA_DIGITS, SUFFIX_F, SUFFIX_L } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: if (*it=='+' || *it=='-') state=BASE_PLUSMINUS; else if (*it=='.') state=LEADING_DECIMAL; else if (std::isdigit(static_cast(*it))) state=BASE_DIGITS1; else return false; break; case BASE_PLUSMINUS: if (*it=='.') state=LEADING_DECIMAL; else if (std::isdigit(static_cast(*it))) state=BASE_DIGITS1; else if (*it=='e' || *it=='E') state=E; else return false; break; case LEADING_DECIMAL: if (std::isdigit(static_cast(*it))) state=BASE_DIGITS2; else if (*it=='e' || *it=='E') state=E; else return false; break; case BASE_DIGITS1: if (*it=='e' || *it=='E') state=E; else if (*it=='.') state=TRAILING_DECIMAL; else if (!std::isdigit(static_cast(*it))) return false; break; case TRAILING_DECIMAL: if (*it=='e' || *it=='E') state=E; else if (*it=='f' || *it=='F') state=SUFFIX_F; else if (*it=='l' || *it=='L') state=SUFFIX_L; else if (std::isdigit(static_cast(*it))) state=BASE_DIGITS2; else return false; break; case BASE_DIGITS2: if (*it=='e' || *it=='E') state=E; else if (*it=='f' || *it=='F') state=SUFFIX_F; else if (*it=='l' || *it=='L') state=SUFFIX_L; else if (!std::isdigit(static_cast(*it))) return false; break; case E: if (*it=='+' || *it=='-') state=MANTISSA_PLUSMINUS; else if (std::isdigit(static_cast(*it))) state=MANTISSA_DIGITS; else return false; break; case MANTISSA_PLUSMINUS: if (!std::isdigit(static_cast(*it))) return false; else state=MANTISSA_DIGITS; break; case MANTISSA_DIGITS: if (*it=='f' || *it=='F') state=SUFFIX_F; else if (*it=='l' || *it=='L') state=SUFFIX_L; else if (!std::isdigit(static_cast(*it))) return false; break; case SUFFIX_F: return false; case SUFFIX_L: return false; } } return (state==BASE_DIGITS2 || state==MANTISSA_DIGITS || state==TRAILING_DECIMAL || state==SUFFIX_F || 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); } /*! \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 Status { START, PLUSMINUS, OCTAL_PREFIX, DIGITS } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: if (*it == '+' || *it == '-') state = PLUSMINUS; else if (*it == '0') state = OCTAL_PREFIX; else return false; break; case PLUSMINUS: if (*it == '0') state = OCTAL_PREFIX; else return false; break; case OCTAL_PREFIX: if (isOctalDigit(static_cast(*it))) state = DIGITS; else return false; break; case DIGITS: if (isOctalDigit(static_cast(*it))) state = DIGITS; else return isValidIntegerSuffix(it,str.end()); break; } } return state == DIGITS; } bool MathLib::isIntHex(const std::string& str) { enum Status { START, PLUSMINUS, HEX_PREFIX, DIGIT, DIGITS } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: if (*it == '+' || *it == '-') state = PLUSMINUS; else if (*it == '0') state = HEX_PREFIX; else return false; break; case PLUSMINUS: if (*it == '0') state = HEX_PREFIX; else return false; break; case HEX_PREFIX: if (*it == 'x' || *it == 'X') state = DIGIT; else return false; break; case DIGIT: if (isxdigit(static_cast(*it))) state = DIGITS; else return false; break; case DIGITS: if (isxdigit(static_cast(*it))) state = DIGITS; else return isValidIntegerSuffix(it,str.end()); break; } } return state == DIGITS; } bool MathLib::isFloatHex(const std::string& str) { enum Status { START, PLUSMINUS, HEX_PREFIX, WHOLE_NUMBER_DIGIT, WHOLE_NUMBER_DIGITS, FRACTION, EXPONENT_DIGIT, EXPONENT_DIGITS } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: if (*it == '+' || *it == '-') state = PLUSMINUS; else if (*it == '0') state = HEX_PREFIX; else return false; break; case PLUSMINUS: if (*it == '0') state = HEX_PREFIX; else return false; break; case HEX_PREFIX: if (*it == 'x' || *it == 'X') state = WHOLE_NUMBER_DIGIT; else return false; break; case WHOLE_NUMBER_DIGIT: if (isxdigit(static_cast(*it))) state = WHOLE_NUMBER_DIGITS; else return false; break; case WHOLE_NUMBER_DIGITS: if (isxdigit(static_cast(*it))) state = WHOLE_NUMBER_DIGITS; else if (*it=='.') state = FRACTION; else if (*it=='p' || *it=='P') state = EXPONENT_DIGIT; else return false; break; case FRACTION: if (isxdigit(static_cast(*it))) state = FRACTION; else if (*it=='p' || *it=='P') state = EXPONENT_DIGIT; break; case EXPONENT_DIGIT: if (isxdigit(static_cast(*it))) state = EXPONENT_DIGITS; else if (*it=='+' || *it=='-') state = EXPONENT_DIGITS; else return false; break; case EXPONENT_DIGITS: if (isxdigit(static_cast(*it))) state = EXPONENT_DIGITS; else return *it=='f'||*it=='F'||*it=='l'||*it=='L'; break; } } return state==EXPONENT_DIGITS; } bool MathLib::isValidIntegerSuffix(const std::string& str) { return isValidIntegerSuffix(str.begin(), str.end()); } bool MathLib::isValidIntegerSuffix(std::string::const_iterator it, std::string::const_iterator end) { 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 (*it == 'i') state = SUFFIX_I; else return false; break; case SUFFIX_U: if (*it == 'l' || *it == 'L') state = SUFFIX_UL; // UL else if (*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)); } /*! \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, PLUSMINUS, GNU_BIN_PREFIX, DIGIT, DIGITS } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: if (*it == '+' || *it == '-') state = PLUSMINUS; else if (*it == '0') state = GNU_BIN_PREFIX; else return false; break; case PLUSMINUS: if (*it == '0') state = GNU_BIN_PREFIX; else return false; break; case GNU_BIN_PREFIX: if (*it == 'b' || *it == 'B') state = DIGIT; else return false; break; case DIGIT: if (*it == '0' || *it == '1') state = DIGITS; else return false; break; case DIGITS: if (*it == '0' || *it == '1') state = DIGITS; else return isValidIntegerSuffix(it,str.end()); break; } } return state == DIGITS; } bool MathLib::isDec(const std::string & str) { enum Status { START, PLUSMINUS, DIGIT } state = START; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { switch (state) { case START: if (*it == '+' || *it == '-') state = PLUSMINUS; else if (isdigit(static_cast(*it))) state = DIGIT; else return false; break; case PLUSMINUS: if (isdigit(static_cast(*it))) state = DIGIT; else return false; break; case DIGIT: if (isdigit(static_cast(*it))) state = DIGIT; else return isValidIntegerSuffix(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) { 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.size() < 1 || (str[0] != '.' && str[0] != '-' && str[0] != '+')))) return false; // Has to be a number for (size_t i = 0; i < str.size(); i++) { if (std::isdigit(static_cast(str[i])) && str[i] != '0') // May not contain digits other than 0 return false; if (str[i] == 'E' || str[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.82/lib/mathlib.h000066400000000000000000000145561322667425100156060ustar00rootroot00000000000000/* * 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 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 intValue; double doubleValue; enum { INT, LONG, LONGLONG, FLOAT } type; bool isUnsigned; void promote(const value &v); public: explicit value(const std::string &s); std::string str() const; bool isInt() const { return type != FLOAT; } bool isFloat() const { return type == FLOAT; } double getDoubleValue() const { return isFloat() ? doubleValue : (double)intValue; } 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); static bool isValidIntegerSuffix(const std::string& str); static bool isValidIntegerSuffix(std::string::const_iterator it, std::string::const_iterator end); 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.82/lib/path.cpp000066400000000000000000000153361322667425100154520ustar00rootroot00000000000000/* * 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 . */ #if defined(__GNUC__) && (defined(_WIN32) || defined(__CYGWIN__)) #undef __STRICT_ANSI__ #endif #include "path.h" #include "utils.h" #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) != 0) #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 (std::vector::const_iterator i = basePaths.begin(); i != basePaths.end(); ++i) { if (absolutePath == *i || i->empty()) // Seems to be a file, or path is empty continue; bool endsWithSep = endsWith(*i,'/'); if (absolutePath.compare(0, i->length(), *i) == 0 && absolutePath[i->length() - (endsWithSep?1:0)] == '/') { std::string rest = absolutePath.substr(i->length() + (endsWithSep?0:1)); return rest; } } 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__) 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; } cppcheck-1.82/lib/path.h000066400000000000000000000146221322667425100151140ustar00rootroot00000000000000/* * 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 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); }; /// @} //--------------------------------------------------------------------------- #endif // pathH cppcheck-1.82/lib/pathmatch.cpp000066400000000000000000000062661322667425100164710ustar00rootroot00000000000000/* * 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 "pathmatch.h" #include "path.h" #include "utils.h" #include #include #include PathMatch::PathMatch(const std::vector &excludedPaths, bool caseSensitive) : _excludedPaths(excludedPaths), _caseSensitive(caseSensitive) { if (!_caseSensitive) for (std::vector::iterator i = _excludedPaths.begin(); i != _excludedPaths.end(); ++i) std::transform(i->begin(), i->end(), i->begin(), ::tolower); _workingDirectory.push_back(Path::getCurrentPath()); } bool PathMatch::match(const std::string &path) const { if (path.empty()) return false; for (std::vector::const_iterator i = _excludedPaths.begin(); i != _excludedPaths.end(); ++i) { const std::string excludedPath((!Path::isAbsolute(path) && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, _workingDirectory) : *i); std::string findpath = Path::fromNativeSeparators(path); if (!_caseSensitive) 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.82/lib/pathmatch.h000066400000000000000000000034771322667425100161370ustar00rootroot00000000000000/* * 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 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 _excludedPaths; bool _caseSensitive; std::vector _workingDirectory; }; /// @} #endif // PATHMATCH_H cppcheck-1.82/lib/pcrerules.pri000066400000000000000000000007241322667425100165250ustar00rootroot00000000000000# 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.82/lib/platform.cpp000066400000000000000000000160451322667425100163400ustar00rootroot00000000000000/* * 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 "platform.h" #include "tinyxml2.h" #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::platformFile(const std::string &filename) { // open file.. tinyxml2::XMLDocument doc; if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) return false; 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(node->Name(), "short") == 0) sizeof_short = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "int") == 0) sizeof_int = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "long") == 0) sizeof_long = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "long-long") == 0) sizeof_long_long = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "float") == 0) sizeof_float = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "double") == 0) sizeof_double = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "long-double") == 0) sizeof_long_double = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "pointer") == 0) sizeof_pointer = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "size_t") == 0) sizeof_size_t = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "wchar_t") == 0) sizeof_wchar_t = std::atoi(node->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.82/lib/platform.h000066400000000000000000000106101322667425100157750ustar00rootroot00000000000000/* * 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 platformH #define platformH //--------------------------------------------------------------------------- #include "config.h" #include /// @addtogroup Core /// @{ namespace cppcheck { /** * @brief Platform settings */ class CPPCHECKLIB Platform { private: static long long min_value(int bit) { if (bit >= 64) return 1LL << 63; 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); } unsigned int char_bit; /// bits in char unsigned int short_bit; /// bits in short unsigned int int_bit; /// bits in int unsigned int long_bit; /// bits in long unsigned int long_long_bit; /// bits in long long /** size of standard types */ unsigned int sizeof_bool; unsigned int sizeof_short; unsigned int sizeof_int; unsigned int sizeof_long; unsigned int sizeof_long_long; unsigned int sizeof_float; unsigned int sizeof_double; unsigned int sizeof_long_double; unsigned int sizeof_wchar_t; unsigned int sizeof_size_t; unsigned 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); /** set the platform type for user specified platforms */ bool platformFile(const std::string &filename); /** * @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 { switch (platformType) { 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.82/lib/preprocessor.cpp000066400000000000000000001110271322667425100172360ustar00rootroot00000000000000/* * 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 "preprocessor.h" #include "errorlogger.h" #include "library.h" #include "path.h" #include "settings.h" #include "simplecpp.h" #include "suppressions.h" #include #include #include #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)) { } bool Preprocessor::missingIncludeFlag; bool Preprocessor::missingSystemIncludeFlag; char Preprocessor::macroChar = char(1); Preprocessor::Preprocessor(Settings& settings, ErrorLogger *errorLogger) : _settings(settings), _errorLogger(errorLogger) { } Preprocessor::~Preprocessor() { for (std::map::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) delete it->second; } static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &_settings) { std::list suppressionIDs; for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (tok->comment) { std::istringstream iss(tok->str.substr(2)); std::string word; iss >> word; if (word != "cppcheck-suppress") continue; iss >> word; if (iss) suppressionIDs.push_back(word); continue; } if (suppressionIDs.empty()) continue; // Relative filename std::string relativeFilename(tok->location.file()); if (_settings.relativePaths) { for (std::size_t j = 0U; j < _settings.basePaths.size(); ++j) { const std::string bp = _settings.basePaths[j] + "/"; if (relativeFilename.compare(0,bp.size(),bp)==0) { relativeFilename = relativeFilename.substr(bp.size()); } } } // Add the suppressions. for (std::list::const_iterator it = suppressionIDs.begin(); it != suppressionIDs.end(); ++it) { _settings.nomsg.addSuppression(*it, relativeFilename, tok->location.line); } suppressionIDs.clear(); } } void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens) { if (!_settings.inlineSuppressions) return; ::inlineSuppressions(tokens, _settings); for (std::map::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) { if (it->second) ::inlineSuppressions(*it->second, _settings); } } void Preprocessor::setDirectives(const simplecpp::TokenList &tokens) { // directive list.. directives.clear(); std::vector list; list.reserve(1U + tokenlists.size()); list.push_back(&tokens); for (std::map::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) { list.push_back(it->second); } for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) { for (const simplecpp::Token *tok = (*it)->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; } directives.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 (std::set::const_iterator it = configset.begin(); it != configset.end(); ++it) { if (!cfg.empty()) cfg += ';'; cfg += *it; } 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 (std::set::const_iterator it = configs2.begin(); it != configs2.end(); ++it) { if (it->empty()) continue; if (*it == "0") return ""; if (hasDefine(userDefines, *it)) continue; if (!ret.empty()) ret += ';'; ret += *it; } 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); 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) { for (unsigned int i = 0; i < configs_if.size(); ++i) { if (hasDefine(userDefines, configs_if[i])) return true; } return false; } 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; ret.insert(""); if (!tokens.cfront()) return ret; std::set defined; defined.insert("__cplusplus"); ::getConfigs(tokens, defined, _settings.userDefines, _settings.userUndefs, ret); for (std::map::const_iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) { if (!_settings.configurationExcluded(it->first)) ::getConfigs(*(it->second), defined, _settings.userDefines, _settings.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 (std::set::const_iterator it = configs.begin(); it != configs.end(); ++it) { if (_settings.userUndefs.find(*it) == _settings.userUndefs.end()) { result[ *it ] = getcode(tokens1, *it, 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 (file0.empty()) file0 = filename; simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList); const std::set configs = getConfigs(tokens1); for (std::set::const_iterator it = configs.begin(); it != configs.end(); ++it) resultConfigurations.push_back(*it); 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 &_settings, const std::string &cfg, const std::string &filename) { simplecpp::DUI dui; splitcfg(_settings.userDefines, dui.defines, "1"); if (!cfg.empty()) splitcfg(cfg, dui.defines, emptyString); for (std::vector::const_iterator it = _settings.library.defines.begin(); it != _settings.library.defines.end(); ++it) { if (it->compare(0,8,"#define ")!=0) continue; std::string s = it->substr(8); std::string::size_type pos = s.find_first_of(" ("); if (pos == std::string::npos) { dui.defines.push_back(s); continue; } if (s[pos] == ' ') { s[pos] = '='; } else { s[s.find(')')+1] = '='; } dui.defines.push_back(s); } if (Path::isCPP(filename)) dui.defines.push_back("__cplusplus"); dui.undefined = _settings.userUndefs; // -U dui.includePaths = _settings.includePaths; // -I dui.includes = _settings.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: return true; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: break; }; } return false; } void Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files) { const simplecpp::DUI dui = createDUI(_settings, emptyString, files[0]); tokenlists = simplecpp::load(rawtokens, files, dui, nullptr); } void Preprocessor::removeComments() { for (std::map::iterator it = tokenlists.begin(); it != tokenlists.end(); ++it) { if (it->second) it->second->removeComments(); } } void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const { tokens->sizeOfType["bool"] = _settings.sizeof_bool; tokens->sizeOfType["short"] = _settings.sizeof_short; tokens->sizeOfType["int"] = _settings.sizeof_int; tokens->sizeOfType["long"] = _settings.sizeof_long; tokens->sizeOfType["long long"] = _settings.sizeof_long_long; tokens->sizeOfType["float"] = _settings.sizeof_float; tokens->sizeOfType["double"] = _settings.sizeof_double; tokens->sizeOfType["long double"] = _settings.sizeof_long_double; tokens->sizeOfType["bool *"] = _settings.sizeof_pointer; tokens->sizeOfType["short *"] = _settings.sizeof_pointer; tokens->sizeOfType["int *"] = _settings.sizeof_pointer; tokens->sizeOfType["long *"] = _settings.sizeof_pointer; tokens->sizeOfType["long long *"] = _settings.sizeof_pointer; tokens->sizeOfType["float *"] = _settings.sizeof_pointer; tokens->sizeOfType["double *"] = _settings.sizeof_pointer; tokens->sizeOfType["long double *"] = _settings.sizeof_pointer; } simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files) { const simplecpp::DUI dui = createDUI(_settings, cfg, files[0]); simplecpp::OutputList outputList; std::list macroUsage; simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens1, files, tokenlists, dui, &outputList, ¯oUsage); bool showerror = (!_settings.userDefines.empty() && !_settings.force); reportOutput(outputList, showerror); if (hasErrors(outputList)) return simplecpp::TokenList(files); 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); 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 ""; return getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos); } void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror) { for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) { switch (it->type) { case simplecpp::Output::ERROR: if (it->msg.compare(0,6,"#error")!=0 || showerror) error(it->location.file(), it->location.line, it->msg); break; case simplecpp::Output::WARNING: case simplecpp::Output::PORTABILITY_BACKSLASH: break; case simplecpp::Output::MISSING_HEADER: { const std::string::size_type pos1 = it->msg.find_first_of("<\""); const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U); if (pos1 < pos2 && pos2 != std::string::npos) missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader); } break; case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: error(it->location.file(), it->location.line, it->msg); break; }; } } void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg) { std::list locationList; if (!filename.empty()) { ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr); locationList.push_back(loc); } _errorLogger->reportErr(ErrorLogger::ErrorMessage(locationList, file0, 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); if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr)) return; if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr)) return; if (headerType == SystemHeader) missingSystemIncludeFlag = true; else missingIncludeFlag = true; if (_errorLogger && _settings.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, file0, 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); _errorLogger->reportInfo(errmsg); } } bool Preprocessor::validateCfg(const std::string &cfg, const std::list ¯oUsageList) { bool ret = true; std::list defines; splitcfg(cfg, defines, emptyString); for (std::list::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) { if (defineIt->find('=') != std::string::npos) continue; const std::string macroName(defineIt->substr(0, defineIt->find('('))); for (std::list::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) { const simplecpp::MacroUsage &mu = *usageIt; if (mu.macroName != macroName) continue; bool directiveLocation = false; for (std::list::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) { if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) { directiveLocation = true; break; } } if (!directiveLocation) { if (_settings.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; ErrorLogger::ErrorMessage::FileLocation loc(file, line); locationList.push_back(loc); ErrorLogger::ErrorMessage errmsg(locationList, file0, 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); _errorLogger->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 (std::list::const_iterator it = directives.begin(); it != directives.end(); ++it) { out << " file << "\" " << "linenr=\"" << it->linenr << "\" " // str might contain characters such as '"', '<' or '>' which // could result in invalid XML, so run it through toxml(). << "str=\"" << ErrorLogger::toxml(it->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 (std::string::const_iterator c = data.begin(); c != data.end(); ++c) { 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 = tokenlists.begin(); it != tokenlists.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 = tokenlists.begin(); it != tokenlists.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.82/lib/preprocessor.h000066400000000000000000000176641322667425100167170ustar00rootroot00000000000000/* * 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 preprocessorH #define preprocessorH //--------------------------------------------------------------------------- #include "config.h" #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 bool missingIncludeFlag; static bool 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 directives; } std::set getConfigs(const simplecpp::TokenList &tokens) const; void 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); 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) { file0 = 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& _settings; ErrorLogger *_errorLogger; /** list of all directives met while preprocessing file */ std::list directives; std::map tokenlists; /** filename for cpp/c file - useful when reporting errors */ std::string file0; }; /// @} //--------------------------------------------------------------------------- #endif // preprocessorH cppcheck-1.82/lib/settings.cpp000066400000000000000000000101751322667425100163520ustar00rootroot00000000000000/* * 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 "settings.h" #include "valueflow.h" bool Settings::_terminated; Settings::Settings() : _enabled(0), debug(false), debugnormal(false), debugwarnings(false), dump(false), exceptionHandling(false), inconclusive(false), jointSuppressionReport(false), experimental(false), quiet(false), inlineSuppressions(false), verbose(false), force(false), relativePaths(false), xml(false), xml_version(2), jobs(1), loadAverage(0), exitCode(0), showtime(SHOWTIME_NONE), preprocessOnly(false), maxConfigs(12), enforcedLang(None), reportProgress(false), checkConfiguration(false), checkLibrary(false) { } 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") { _enabled |= WARNING | STYLE | PERFORMANCE | PORTABILITY | INFORMATION | UNUSED_FUNCTION | MISSING_INCLUDE; } else if (str == "warning") { _enabled |= WARNING; } else if (str == "style") { _enabled |= STYLE; } else if (str == "performance") { _enabled |= PERFORMANCE; } else if (str == "portability") { _enabled |= PORTABILITY; } else if (str == "information") { _enabled |= INFORMATION | MISSING_INCLUDE; } else if (str == "unusedFunction") { _enabled |= UNUSED_FUNCTION; } else if (str == "missingInclude") { _enabled |= MISSING_INCLUDE; } #ifdef CHECK_INTERNAL else if (str == "internal") { _enabled |= 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.82/lib/settings.h000066400000000000000000000177321322667425100160250ustar00rootroot00000000000000/* * 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 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 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 _enabled; /** @brief terminate checking */ static bool _terminated; public: Settings(); /** @brief --cppcheck-build-dir */ std::string buildDir; /** @brief Is --debug given? */ bool debug; /** @brief Is --debug-normal given? */ bool debugnormal; /** @brief Is --debug-warnings given? */ bool debugwarnings; /** @brief Is --dump given? */ bool dump; std::string dumpFile; /** @brief Is --exception-handling given */ bool exceptionHandling; /** @brief Inconclusive checks */ bool inconclusive; /** @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; /** * 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 Is --quiet given? */ bool quiet; /** @brief Is --inline-suppr given? */ bool inlineSuppressions; /** @brief Is --verbose given? */ bool verbose; /** @brief Request termination of checking */ static void terminate(bool t = true) { Settings::_terminated = t; } /** @brief termination requested? */ static bool terminated() { return Settings::_terminated; } /** @brief Force checking the files with "too many" configurations (--force). */ bool force; /** @brief Use relative paths in output. */ bool relativePaths; /** @brief Paths used as base for conversion to relative paths. */ std::vector basePaths; /** @brief write results (--output-file=<file>) */ std::string outputFile; /** @brief plist output (--plist-output=<dir>) */ std::string plistOutput; /** @brief write XML results (--xml) */ bool xml; /** @brief XML version (--xml-version=..) */ int xml_version; /** @brief How many processes/threads should do checking at the same time. Default is 1. (-j N) */ unsigned int jobs; /** @brief Load average value */ unsigned int loadAverage; /** @brief If errors are found, this value is returned from main(). Default value is 0. */ int exitCode; /** @brief The output format in which the errors are printed in text mode, e.g. "{severity} {file}:{line} {message} {id}" */ std::string outputFormat; /** @brief show timing information (--showtime=file|summary|top5) */ SHOWTIME_MODES showtime; /** @brief Using -E for debugging purposes */ bool preprocessOnly; /** @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 Maximum number of configurations to check before bailing. Default is 12. (--max-configs=N) */ unsigned int maxConfigs; /** * @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 (_enabled & 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; /** * @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() { _enabled = 0; } enum Language { None, C, CPP }; /** @brief Name of the language that is enforced. Empty per default. */ Language enforcedLang; /** @brief suppress message (--suppressions) */ Suppressions nomsg; /** @brief suppress exitcode */ Suppressions nofail; /** @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 include paths excluded from checking the configuration */ std::set configExcludePaths; /** @brief --report-progress */ bool reportProgress; /** Library (--library) */ Library library; /** 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; /** Is the 'configuration checking' wanted? */ bool checkConfiguration; /** Check for incomplete info in library files? */ bool checkLibrary; /** Struct contains standards settings */ Standards standards; ImportProject project; /** * @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 (std::set::const_iterator i=configExcludePaths.begin(); i!=configExcludePaths.end(); ++i) { if (file.length()>=i->length() && file.compare(0,i->length(),*i)==0) { return true; } } return false; } }; /// @} //--------------------------------------------------------------------------- #endif // settingsH cppcheck-1.82/lib/standards.h000066400000000000000000000044671322667425100161510ustar00rootroot00000000000000/* * 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 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, CPPLatest=CPP14 } cpp; /** Code is posix */ bool posix; /** This constructor clear all the variables **/ Standards() : c(C11), cpp(CPP14), posix(false) {} 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; } 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; } return false; } }; /// @} //--------------------------------------------------------------------------- #endif // standardsH cppcheck-1.82/lib/suppressions.cpp000066400000000000000000000241601322667425100172660ustar00rootroot00000000000000/* * 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 "suppressions.h" #include "path.h" #include #include // std::isdigit, std::isalnum, etc #include #include #include 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() >= 2 && line[0] == '/' && line[1] == '/') continue; const std::string errmsg(addSuppressionLine(line)); if (!errmsg.empty()) return errmsg; } return ""; } std::string Suppressions::addSuppressionLine(const std::string &line) { std::istringstream lineStream(line); std::string id; std::string file; unsigned int lineNumber = 0; if (std::getline(lineStream, id, ':')) { if (std::getline(lineStream, file)) { // 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 = file.rfind(':'); // if a colon is found and there is no dot after it.. if (pos != std::string::npos && file.find('.', pos) == std::string::npos) { // Try to parse out the line number try { std::istringstream istr1(file.substr(pos+1)); istr1 >> lineNumber; } catch (...) { lineNumber = 0; } if (lineNumber > 0) { file.erase(pos); } } } } // We could perhaps check if the id is valid and return error if it is not const std::string errmsg(addSuppression(id, Path::fromNativeSeparators(file), lineNumber)); if (!errmsg.empty()) return errmsg; return ""; } bool Suppressions::FileMatcher::match(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 { 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++; } } std::string Suppressions::FileMatcher::addFile(const std::string &name, unsigned int line) { if (name.find_first_of("*?") != std::string::npos) { for (std::string::const_iterator i = name.begin(); i != name.end(); ++i) { if (*i == '*') { std::string::const_iterator j = i + 1; if (j != name.end() && (*j == '*' || *j == '?')) { return "Failed to add suppression. Syntax error in glob."; } } } _globs[name][line] = false; } else if (name.empty()) { _globs["*"][0U] = false; } else { _files[Path::simplifyPath(name)][line] = false; } return ""; } bool Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line) { if (isSuppressedLocal(file, line)) return true; for (std::map >::iterator g = _globs.begin(); g != _globs.end(); ++g) { if (match(g->first, file)) { std::map::iterator l = g->second.find(0U); if (l != g->second.end()) { l->second = true; return true; } l = g->second.find(line); if (l != g->second.end()) { l->second = true; return true; } } } return false; } bool Suppressions::FileMatcher::isSuppressedLocal(const std::string &file, unsigned int line) { std::map >::iterator f = _files.find(Path::fromNativeSeparators(file)); if (f != _files.end()) { std::map::iterator l = f->second.find(0U); if (l != f->second.end()) { l->second = true; return true; } l = f->second.find(line); if (l != f->second.end()) { l->second = true; return true; } } return false; } std::string Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line) { // Check that errorId is valid.. if (errorId.empty()) { return "Failed to add suppression. No id."; } if (errorId != "*") { for (std::string::size_type pos = 0; pos < errorId.length(); ++pos) { if (errorId[pos] < 0 || (!std::isalnum(errorId[pos]) && errorId[pos] != '_')) { return "Failed to add suppression. Invalid id \"" + errorId + "\""; } if (pos == 0 && std::isdigit(errorId[pos])) { return "Failed to add suppression. Invalid id \"" + errorId + "\""; } } } return _suppressions[errorId].addFile(file, line); } bool Suppressions::isSuppressed(const std::string &errorId, const std::string &file, unsigned int line) { if (errorId != "unmatchedSuppression" && _suppressions.find("*") != _suppressions.end()) if (_suppressions["*"].isSuppressed(file, line)) return true; std::map::iterator suppression = _suppressions.find(errorId); if (suppression == _suppressions.end()) return false; return suppression->second.isSuppressed(file, line); } bool Suppressions::isSuppressedLocal(const std::string &errorId, const std::string &file, unsigned int line) { if (errorId != "unmatchedSuppression" && _suppressions.find("*") != _suppressions.end()) if (_suppressions["*"].isSuppressedLocal(file, line)) return true; std::map::iterator suppression = _suppressions.find(errorId); if (suppression == _suppressions.end()) return false; return suppression->second.isSuppressedLocal(file, line); } std::list Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const { std::list result; for (std::map::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i) { if (!unusedFunctionChecking && i->first == "unusedFunction") continue; std::map >::const_iterator f = i->second._files.find(Path::fromNativeSeparators(file)); if (f != i->second._files.end()) { for (std::map::const_iterator l = f->second.begin(); l != f->second.end(); ++l) { if (!l->second) { result.push_back(SuppressionEntry(i->first, f->first, l->first)); } } } } return result; } std::list Suppressions::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const { std::list result; for (std::map::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i) { if (!unusedFunctionChecking && i->first == "unusedFunction") continue; // global suppressions.. for (std::map >::const_iterator g = i->second._globs.begin(); g != i->second._globs.end(); ++g) { for (std::map::const_iterator l = g->second.begin(); l != g->second.end(); ++l) { if (!l->second) { result.push_back(SuppressionEntry(i->first, g->first, l->first)); } } } } return result; } cppcheck-1.82/lib/suppressions.h000066400000000000000000000131351322667425100167330ustar00rootroot00000000000000/* * 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 suppressionsH #define suppressionsH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include /// @addtogroup Core /// @{ /** @brief class for handling suppressions */ class CPPCHECKLIB Suppressions { private: class CPPCHECKLIB FileMatcher { friend class Suppressions; private: /** @brief List of filenames suppressed, bool flag indicates whether suppression matched. */ std::map > _files; /** @brief List of globs suppressed, bool flag indicates whether suppression matched. */ std::map > _globs; /** * @brief Match a name against a glob pattern. * @param pattern The glob pattern to match. * @param name The filename to match against the glob pattern. * @return match success */ static bool match(const std::string &pattern, const std::string &name); public: /** * @brief Add a file or glob (and line number). * @param name File name or glob pattern * @param line Line number * @return error message. empty upon success */ std::string addFile(const std::string &name, unsigned int line); /** * @brief Returns true if the file name matches a previously added file or glob pattern. * @param file File name to check * @param line Line number * @return true if this filename/line matches */ bool isSuppressed(const std::string &file, unsigned int line); /** * @brief Returns true if the file name matches a previously added file (only, not glob pattern). * @param file File name to check * @param line Line number * @return true if this filename/line matches */ bool isSuppressedLocal(const std::string &file, unsigned int line); }; /** @brief List of error which the user doesn't want to see. */ std::map _suppressions; public: /** * @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 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 errorId the id for the error, e.g. "arrayIndexOutOfBounds" * @param file File name with the path, e.g. "src/main.cpp" * @param line number, e.g. "123" * @return error message. empty upon success */ std::string addSuppression(const std::string &errorId, const std::string &file = emptyString, unsigned int line = 0); /** * @brief Returns true if this message should not be shown to the user. * @param errorId the id for the error, e.g. "arrayIndexOutOfBounds" * @param file File name with the path, e.g. "src/main.cpp" * @param line number, e.g. "123" * @return true if this error is suppressed. */ bool isSuppressed(const std::string &errorId, const std::string &file, unsigned int line); /** * @brief Returns true if this message should not be shown to the user (explicit files only, not glob patterns). * @param errorId the id for the error, e.g. "arrayIndexOutOfBounds" * @param file File name with the path, e.g. "src/main.cpp" * @param line number, e.g. "123" * @return true if this error is suppressed. */ bool isSuppressedLocal(const std::string &errorId, const std::string &file, unsigned int line); struct SuppressionEntry { SuppressionEntry(const std::string &aid, const std::string &afile, unsigned int aline) : id(aid), file(afile), line(aline) { } std::string id; std::string file; unsigned int line; }; /** * @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; }; /// @} //--------------------------------------------------------------------------- #endif // suppressionsH cppcheck-1.82/lib/symboldatabase.cpp000066400000000000000000006504671322667425100175220ustar00rootroot00000000000000/* * 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 "symboldatabase.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) : _tokenizer(tokenizer), _settings(settings), _errorLogger(errorLogger) { cpp = isCPP(); if (_settings->defaultSign == 's' || _settings->defaultSign == 'S') defaultSignedness = ValueType::SIGNED; else if (_settings->defaultSign == 'u' || _settings->defaultSign == 'U') defaultSignedness = ValueType::UNSIGNED; else defaultSignedness = ValueType::UNKNOWN_SIGN; createSymbolDatabaseFindAllScopes(); createSymbolDatabaseClassInfo(); createSymbolDatabaseVariableInfo(); createSymbolDatabaseCopyAndMoveConstructors(); createSymbolDatabaseFunctionScopes(); createSymbolDatabaseClassAndStructScopes(); createSymbolDatabaseFunctionReturnTypes(); createSymbolDatabaseNeedInitialization(); createSymbolDatabaseVariableSymbolTable(); createSymbolDatabaseSetScopePointers(); createSymbolDatabaseSetFunctionPointers(true); createSymbolDatabaseSetVariablePointers(); createSymbolDatabaseSetTypePointers(); createSymbolDatabaseEnums(); createSymbolDatabaseUnknownArrayDimensions(); } void SymbolDatabase::createSymbolDatabaseFindAllScopes() { // create global scope scopeList.push_back(Scope(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 = _tokenizer->tokens(); tok; tok = tok ? tok->next() : nullptr) { // #5593 suggested to add here: if (_errorLogger) _errorLogger->reportProgress(_tokenizer->list.getSourceFilePath(), "SymbolDatabase", tok->progressValue()); // Locate next class if ((_tokenizer->isCPP() && ((Token::Match(tok, "class|struct|union|namespace ::| %name% {|:|::|<") && tok->strAt(-1) != "friend") || (Token::Match(tok, "enum class| %name% {") || Token::Match(tok, "enum class| %name% : %name% {")))) || (_tokenizer->isC() && Token::Match(tok, "struct|union|enum %name% {"))) { const Token *tok2 = tok->tokAt(2); if (tok->strAt(1) == "::") tok2 = tok2->next(); else if (_tokenizer->isCPP() && tok->strAt(1) == "class") tok2 = tok2->next(); while (tok2 && tok2->str() == "::") tok2 = tok2->tokAt(2); // skip over template args if (tok2 && tok2->str() == "<" && tok2->link()) tok2 = tok2->link()->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 break; // bail continue; } break; // bail } Scope *new_scope = findScope(tok->next(), scope); if (new_scope) { // only create base list for classes and structures if (new_scope->isClassOrStruct()) { // goto initial '{' if (!new_scope->definedType) { _tokenizer->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 (_tokenizer->isCPP() && tok->str() == "class") { access[new_scope] = Private; new_scope->type = Scope::eClass; } else if (tok->str() == "struct") { access[new_scope] = Public; new_scope->type = Scope::eStruct; } new_scope->classDef = tok; new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); // make sure we have valid code if (!new_scope->classEnd) { _tokenizer->syntaxError(tok); } scope = new_scope; tok = tok2; } else { scopeList.push_back(Scope(this, tok, scope)); new_scope = &scopeList.back(); if (tok->str() == "class") access[new_scope] = Private; else if (tok->str() == "struct" || tok->str() == "union") access[new_scope] = Public; // fill typeList... if (new_scope->isClassOrStructOrUnion() || new_scope->type == Scope::eEnum) { Type* new_type = findType(tok->next(), scope); if (!new_type) { typeList.push_back(Type(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) { _tokenizer->syntaxError(tok); } } else if (new_scope->type == Scope::eEnum) { if (tok2->str() == ":") tok2 = tok2->tokAt(2); } new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); // make sure we have valid code if (!new_scope->classEnd) { _tokenizer->syntaxError(tok); } if (new_scope->type == Scope::eEnum) { tok2 = new_scope->addEnum(tok, _tokenizer->isCPP()); scope->nestedList.push_back(new_scope); if (!tok2) _tokenizer->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 (_tokenizer->isCPP() && Token::Match(tok, "namespace %name% %type% (") && tok->tokAt(2)->isUpperCaseName() && Token::simpleMatch(tok->linkAt(3), ") {")) { scopeList.push_back(Scope(this, tok, scope)); Scope *new_scope = &scopeList.back(); access[new_scope] = Public; const Token *tok2 = tok->linkAt(3)->next(); new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); // make sure we have valid code if (!new_scope->classEnd) { 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.push_back(Type(tok, nullptr, scope)); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } tok = tok->tokAt(2); } // using namespace else if (_tokenizer->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 (_tokenizer->isCPP() && Token::Match(tok, "using %name% =")) { if (!findType(tok->next(), scope)) { // fill typeList.. typeList.push_back(Type(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.push_back(Scope(this, tok, scope)); Scope *new_scope = &scopeList.back(); access[new_scope] = Public; const Token* varNameTok = tok->next()->link()->next(); if (varNameTok->str() == "*") { varNameTok = varNameTok->next(); } else if (varNameTok->str() == "&") { varNameTok = varNameTok->next(); } typeList.push_back(Type(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, &_settings->library); const Token *tok2 = tok->next(); new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); // make sure we have valid code if (!new_scope->classEnd) { 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.push_back(Scope(this, tok, scope)); Scope *new_scope = &scopeList.back(); access[new_scope] = Public; const Token *tok2 = tok->next(); new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); typeList.push_back(Type(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->classEnd) { 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.push_back(Type(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->classEnd) { 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] = Private; else if (tok->str() == "protected:") access[scope] = Protected; else if (tok->str() == "public:" || tok->str() == "__published:") access[scope] = Public; else if (Token::Match(tok, "public|protected|private %name% :")) { if (tok->str() == "private") access[scope] = Private; else if (tok->str() == "protected") access[scope] = Protected; else access[scope] = 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; // save the function definition argument start '(' function.argDef = argStart; // save the access type function.access = access[scope]; // save the function name location function.tokenDef = funcStart; // save the function parent scope function.nestedIn = scope; // operator function if (function.tokenDef->isOperatorKeyword()) { function.isOperator(true); // 'operator =' is special if (function.tokenDef->str() == "operator=") function.type = Function::eOperatorEqual; } // class constructor/destructor else if (function.tokenDef->str() == scope->className) { // destructor if (function.tokenDef->previous()->str() == "~") function.type = Function::eDestructor; // constructor of any kind else function.type = Function::eConstructor; if (function.tokenDef->previous()->str() == "explicit") function.isExplicit(true); } const Token *tok1 = tok; // look for end of previous statement while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:")) { // virtual function const Token* tok2 = tok1->previous(); if (tok2->str() == "virtual") { function.isVirtual(true); break; } // static function else if (tok2->str() == "static") { function.isStatic(true); break; } // friend function else if (tok2->str() == "friend") { function.isFriend(true); break; } // Function template else if (tok2->link() && tok2->str() == ">" && Token::simpleMatch(tok2->link()->previous(), "template <")) break; tok1 = tok2; } // find the return type if (!function.isConstructor() && !function.isDestructor()) { if (argStart->link()->strAt(1) == ".") // Trailing return type function.retDef = argStart->link()->tokAt(2); else { while (tok1 && Token::Match(tok1->next(), "virtual|static|friend|const|struct|union|enum")) tok1 = tok1->next(); if (tok1) function.retDef = tok1; } } const Token *end = function.argDef->link(); // const function if (end->next()->str() == "const") function.isConst(true); // 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 (_tokenizer->isFunctionHead(end, ";")) { // find the function implementation later tok = end->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() == "&") { function.hasLvalRefQualifier(true); tok = tok->next(); } else if (tok->str() == "&&") { function.hasRvalRefQualifier(true); tok = tok->next(); } else if (tok->str() == "noexcept") { function.isNoExcept(!Token::simpleMatch(tok->next(), "( false )")); tok = tok->next(); if (tok->str() == "(") tok = tok->link()->next(); } else if (Token::simpleMatch(tok, "throw (")) { function.isThrow(true); if (tok->strAt(2) != ")") function.throwArg = end->tokAt(2); tok = tok->linkAt(1)->next(); } if (Token::Match(tok, "= %any% ;")) { const std::string& modifier = tok->strAt(1); function.isPure(modifier == "0"); function.isDefault(modifier == "default"); function.isDelete(modifier == "delete"); tok = tok->tokAt(2); } // skip over unknown tokens while (tok && tok->str() != ";") tok = tok->next(); scope->addFunction(function); } // inline function else { function.isInline(true); function.hasBody(true); if (Token::Match(end, ") const| noexcept")) { int arg = 2; if (end->strAt(1) == "const") arg++; if (end->strAt(arg) == "(") function.noexceptArg = end->tokAt(arg + 1); function.isNoExcept(true); } else if (Token::Match(end, ") const| throw (")) { int arg = 3; if (end->strAt(1) == "const") arg++; if (end->strAt(arg) != ")") function.throwArg = end->tokAt(arg); function.isThrow(true); } else if (Token::Match(end, ") const| &|&&| [;{]")) { int arg = 1; if (end->strAt(arg) == "const") arg++; if (end->strAt(arg) == "&") function.hasLvalRefQualifier(true); else if (end->strAt(arg) == "&&") function.hasRvalRefQualifier(true); } // 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 */ Scope * nested = scope->findInNestedListRecursive(tok->strAt(-2)); if (nested) addClassFunction(&scope, &tok, argStart); else { /** @todo handle friend functions */ } } } // friend class declaration? else if (_tokenizer->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); // save the name if (friendInfo.nameEnd) friendInfo.name = friendInfo.nameEnd->str(); // fill this in after parsing is complete friendInfo.type = nullptr; if (!scope->definedType) _tokenizer->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 { Function* function = addGlobalFunction(scope, tok, argStart, funcStart); if (!function) _tokenizer->syntaxError(tok); // global functions can't be const but we have tests that are if (Token::Match(argStart->link(), ") const| noexcept")) { int arg = 2; if (argStart->link()->strAt(1) == "const") arg++; if (argStart->link()->strAt(arg) == "(") function->noexceptArg = argStart->link()->tokAt(arg + 1); function->isNoExcept(true); } else if (Token::Match(argStart->link(), ") const| throw (")) { int arg = 3; if (argStart->link()->strAt(1) == "const") arg++; if (argStart->link()->strAt(arg) != ")") function->throwArg = argStart->link()->tokAt(arg); function->isThrow(true); } const Token *tok1 = tok->previous(); // look for end of previous statement while (tok1 && !Token::Match(tok1, ";|}|{")) { // static function if (tok1->str() == "static") { function->isStaticLocal(true); break; } // extern function else if (tok1->str() == "extern") { function->isExtern(true); break; } tok1 = tok1->previous(); } } // syntax error? if (!scope) _tokenizer->syntaxError(tok); } // function prototype? else if (declEnd && declEnd->str() == ";") { 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->next(), argStart->next(), emptyString, 0)) { newFunc = false; break; } } // save function prototype in database if (newFunc) { Function* func = addGlobalFunctionDecl(scope, tok, argStart, funcStart); if (Token::Match(argStart->link(), ") const| noexcept")) { int arg = 2; if (argStart->link()->strAt(1) == "const") arg++; if (argStart->link()->strAt(arg) == "(") func->noexceptArg = argStart->link()->tokAt(arg + 1); func->isNoExcept(true); } else if (Token::Match(argStart->link(), ") const| throw (")) { int arg = 3; if (argStart->link()->strAt(1) == "const") arg++; if (argStart->link()->strAt(arg) != ")") func->throwArg = argStart->link()->tokAt(arg); func->isThrow(true); } const Token *tok1 = tok->previous(); // look for end of previous statement while (tok1 && !Token::Match(tok1, ";|}|{")) { // extern function if (tok1->str() == "extern") { func->isExtern(true); break; } tok1 = tok1->previous(); } } tok = declEnd; continue; } } } else if (scope->isExecutable()) { if (Token::Match(tok, "else|try|do {")) { const Token* tok1 = tok->next(); if (tok->str() == "else") scopeList.push_back(Scope(this, tok, scope, Scope::eElse, tok1)); else if (tok->str() == "do") scopeList.push_back(Scope(this, tok, scope, Scope::eDo, tok1)); else //if (tok->str() == "try") scopeList.push_back(Scope(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.push_back(Scope(this, tok, scope, Scope::eIf, scopeStartTok)); else if (tok->str() == "for") { scopeList.push_back(Scope(this, tok, scope, Scope::eFor, scopeStartTok)); } else if (tok->str() == "while") scopeList.push_back(Scope(this, tok, scope, Scope::eWhile, scopeStartTok)); else if (tok->str() == "catch") { scopeList.push_back(Scope(this, tok, scope, Scope::eCatch, scopeStartTok)); } else // if (tok->str() == "switch") scopeList.push_back(Scope(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), Local, &_settings->library); // check for variable declaration and add it to new scope if found else if (scope->type == Scope::eCatch) scope->checkVariable(tok->tokAt(2), Throw, &_settings->library); // check for variable declaration and add it to new scope if found tok = scopeStartTok; } else if (tok->str() == "{") { if (tok->previous()->varId()) tok = tok->link(); else { const Token* tok2 = tok->previous(); while (!Token::Match(tok2, ";|}|{|)")) tok2 = tok2->previous(); if (tok2->next() != tok && tok2->strAt(1) != ".") tok2 = nullptr; // No lambda if (tok2 && tok2->str() == ")" && tok2->link()->strAt(-1) == "]") { scopeList.push_back(Scope(this, tok2->link()->linkAt(-1), scope, Scope::eLambda, tok)); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else if (!Token::Match(tok->previous(), "=|,|(|return") && !(tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "=|,|(|return"))) { scopeList.push_back(Scope(this, tok, scope, Scope::eUnconditional, tok)); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else { tok = tok->link(); } } } } } } void SymbolDatabase::createSymbolDatabaseClassInfo() { if (!_tokenizer->isC()) { // 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 Scope *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 (unsigned int i = 0; i < it->derivedFrom.size(); ++i) { const Type* found = findType(it->derivedFrom[i].nameTok, it->enclosingScope); if (found && found->findDependency(&(*it))) { // circular dependency //_tokenizer->syntaxError(nullptr); } else { it->derivedFrom[i].type = found; } } } // fill in friend info for (std::list::iterator it = typeList.begin(); it != typeList.end(); ++it) { for (std::list::iterator i = it->friendList.begin(); i != it->friendList.end(); ++i) { i->type = findType(i->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(&_settings->library); } // 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()) 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 (_tokenizer->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::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) { _blankTypes.push_back(Type()); scope->definedType = &_blankTypes.back(); } if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::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::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::True) needInitialization = true; else if (var->type()->needInitialization == Type::Unknown) unknown = true; } } else if (!var->hasDefault()) needInitialization = true; } if (needInitialization) scope->definedType->needInitialization = Type::True; else if (!unknown) scope->definedType->needInitialization = Type::False; else { if (scope->definedType->needInitialization == Type::Unknown) unknowns++; } } } else if (scope->type == Scope::eUnion && scope->definedType->needInitialization == Type::Unknown) scope->definedType->needInitialization = Type::True; } retry++; } while (unknowns && retry < 100); // this shouldn't happen so output a debug warning if (retry == 100 && _settings->debugwarnings) { for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { const Scope *scope = &(*it); if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::Unknown) debugMessage(scope->classDef, "SymbolDatabase::SymbolDatabase couldn't resolve all user defined types."); } } } } void SymbolDatabase::createSymbolDatabaseVariableSymbolTable() { // create variable symbol table _variableList.resize(_tokenizer->varIdCount() + 1); std::fill_n(_variableList.begin(), _variableList.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) { unsigned int varId = var->declarationId(); if (varId) _variableList[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) _variableList[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->classStart->next(); tok && tok != func->classEnd; 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() && _variableList[tok1->varId()] == 0) { const Variable *var = _variableList[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 _variableList[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->classStart); Token* end = const_cast(it->classEnd); if (it->type == Scope::eGlobal) { start = const_cast(_tokenizer->list.front()); end = const_cast(_tokenizer->list.back()); } assert(start && 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)->classStart) { // 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 = _tokenizer->list.front(); tok != _tokenizer->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->type == Function::eConstructor && 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->classStart) { 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() { // Set type pointers for (const Token* tok = _tokenizer->list.front(); tok != _tokenizer->list.back(); tok = tok->next()) { if (!tok->isName() || tok->varId() || tok->function() || tok->type() || tok->enumerator()) 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(_tokenizer)->newVarId(); _variableList.push_back(membervar); } else _variableList[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(_tokenizer)->newVarId())); _variableList.push_back(membervar); memberId = varId->second.find(membervar->nameToken()->varId()); } else _variableList[membertok->varId()] = membervar; } if (membertok->varId() == 0) membertok->varId(memberId->second); } void SymbolDatabase::createSymbolDatabaseSetVariablePointers() { VarIdMap varIds; // Set variable pointers for (const Token* tok = _tokenizer->list.front(); tok != _tokenizer->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->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 || _variableList[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 || _variableList[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 || _variableList[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 (std::size_t i = 0, end = it->enumeratorList.size(); i < end; ++i) const_cast(it->enumeratorList[i].name)->enumerator(&it->enumeratorList[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 (std::size_t i = 0, end = it->enumeratorList.size(); i < end; ++i) { Enumerator & enumerator = it->enumeratorList[i]; // look for initialization tokens that can be converted to enumerators and convert them if (enumerator.start) { if (!enumerator.end) _tokenizer->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: const Token *rhs = enumerator.start->previous()->astOperand2(); // constant folding of expression: ValueFlow::valueFlowConstantFoldAST(rhs, _settings); // get constant folded value: if (rhs && rhs->values().size() == 1U && rhs->values().front().isKnown()) { 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 = _tokenizer->list.front(); tok != _tokenizer->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::createSymbolDatabaseUnknownArrayDimensions() { // set all unknown array dimensions for (std::size_t i = 1; i <= _tokenizer->varIdCount(); i++) { // check each array variable if (_variableList[i] && _variableList[i]->isArray()) { // check each array dimension const std::vector& dimensions = _variableList[i]->dimensions(); for (std::size_t j = 0; j < dimensions.size(); j++) { Dimension &dimension = const_cast(dimensions[j]); if (dimension.num == 0) { dimension.known = false; // check for a single token dimension if (dimension.start && (dimension.start == dimension.end)) { // check for an enumerator if (dimension.start->enumerator()) { if (dimension.start->enumerator()->value_known) { dimension.num = dimension.start->enumerator()->value; dimension.known = true; } } // check for a variable else if (dimension.start->varId()) { // get maximum size from type // find where this type is defined const Variable *var = getVariableFromVarId(dimension.start->varId()); // make sure it is in the database if (!var) break; // get type token const Token *index_type = var->typeEndToken(); if (index_type->str() == "char") { if (index_type->isUnsigned()) dimension.num = UCHAR_MAX + 1; else if (index_type->isSigned()) dimension.num = SCHAR_MAX + 1; else dimension.num = CHAR_MAX + 1; } else if (index_type->str() == "short") { if (index_type->isUnsigned()) dimension.num = USHRT_MAX + 1; else dimension.num = SHRT_MAX + 1; } // checkScope assumes size is signed int so we limit the following sizes to INT_MAX else if (index_type->str() == "int") { if (index_type->isUnsigned()) dimension.num = UINT_MAX + 1ULL; else dimension.num = INT_MAX + 1ULL; } else if (index_type->str() == "long") { if (index_type->isUnsigned()) { if (index_type->isLong()) dimension.num = ULLONG_MAX; // should be ULLONG_MAX + 1ULL else dimension.num = ULONG_MAX; // should be ULONG_MAX + 1ULL } else { if (index_type->isLong()) dimension.num = LLONG_MAX; // should be LLONG_MAX + 1LL else dimension.num = LONG_MAX; // should be LONG_MAX + 1LL } } } } // check for qualified enumerator else if (dimension.start) { // rhs of [ const Token *rhs = dimension.start->previous()->astOperand2(); // constant folding of expression: ValueFlow::valueFlowConstantFoldAST(rhs, _settings); // get constant folded value: if (rhs && rhs->values().size() == 1U && rhs->values().front().isKnown()) { dimension.num = rhs->values().front().intvalue; dimension.known = true; } } } } } } } SymbolDatabase::~SymbolDatabase() { // Clear scope, type, function and variable pointers for (const Token* tok = _tokenizer->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% ( ... ))( ... ) {' if (tok->str() == "(" && tok->strAt(1) != "*" && tok->link()->previous()->str() == ")") { const Token* tok2 = tok->link()->next(); if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) { const Token* argStartTok = tok->link()->previous()->link(); *funcStart = argStartTok->previous(); *argStart = argStartTok; *declEnd = Token::findmatch(tok2->link()->next(), "{|;"); 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(); // 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 and override specifiers while (Token::Match(tok2, "const|noexcept|throw|override")) { tok2 = tok2->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); } if (tok2 && tok2->str() == ".") { for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, ";|{|=")) 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, "[*&]")) 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(); } // 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 (_tokenizer->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; } _tokenizer->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, &_tokenizer->list, Severity::debug, "symbolDatabaseWarning", msg, false); _errorLogger->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 = _variableList.begin(); iter!=_variableList.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 (_settings->debugwarnings) { validateExecutableScopes(); } //validateVariables(); } 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 Library* lib) { if (_name) setFlag(fIsArray, arrayDimensions(lib)); const Token* tok = _start; while (tok && tok->previous() && tok->previous()->isName()) tok = tok->previous(); const Token* end = _end; 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(_start, "static|const %any%")) _start = _start->next(); while (_end && _end->previous() && _end->str() == "const") _end = _end->previous(); if (_start) { std::string strtype = _start->str(); for (const Token *typeToken = _start; Token::Match(typeToken, "%type% :: %type%"); typeToken = typeToken->tokAt(2)) strtype += "::" + typeToken->strAt(2); setFlag(fIsClass, !lib->podtype(strtype) && !_start->isStandardType() && !isPointer() && !isReference()); setFlag(fIsStlType, Token::simpleMatch(_start, "std ::")); setFlag(fIsStlString, isStlType() && (Token::Match(_start->tokAt(2), "string|wstring|u16string|u32string !!::") || (Token::simpleMatch(_start->tokAt(2), "basic_string <") && !Token::simpleMatch(_start->linkAt(3), "> ::")))); } if (_access == Argument) { tok = _name; if (!tok) { // Argument without name tok = _end; // 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(lib)); } if (!tok) return; tok = tok->next(); while (tok->str() == "[") tok = tok->link(); setFlag(fHasDefault, tok->str() == "="); } // check for C++11 member initialization if (_scope && _scope->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) == _name->str()) || Token::Match(declEnd, "=|{")) setFlag(fHasDefault, true); } if (_start) { if (Token::Match(_start, "float|double")) setFlag(fIsFloatType, true); } } bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, unsigned int depth) { const bool isCPP = scope->check->isCPP(); if (!isCPP) // C does not support overloads return true; // skip "struct" if (first->str() == "struct" || first->str() == "enum") first = first->next(); if (second->str() == "struct" || second->str() == "enum") second = second->next(); // skip const on type passed by value if (Token::Match(first, "const %type% %name%|,|)")) first = first->next(); if (Token::Match(second, "const %type% %name%|,|)")) second = second->next(); while (first->str() == second->str() && first->isLong() == second->isLong() && first->isUnsigned() == second->isUnsigned()) { // 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() != ",") || (first->next()->str() == ")" && 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() != ",") || (second->next()->str() == ")" && 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(), ",|)")); } } // variable with class path else if (depth && Token::Match(first->next(), "%name%")) { std::string param = path + first->next()->str(); if (Token::simpleMatch(second->next(), param.c_str())) { second = second->tokAt(int(depth) * 2); } else if (depth > 1) { std::string short_path = path; // remove last " :: " short_path.resize(short_path.size() - 4); // remove last name std::string::size_type lastSpace = short_path.find_last_of(' '); if (lastSpace != std::string::npos) short_path.resize(lastSpace+1); param = short_path + first->next()->str(); if (Token::simpleMatch(second->next(), param.c_str())) { second = second->tokAt((int(depth) - 1) * 2); } } } // nested or base class variable else if (depth == 0 && Token::Match(first->next(), "%name%") && Token::Match(second->next(), "%name% :: %name%") && ((second->next()->str() == scope->className) || (scope->definedType && scope->definedType->isDerivedFrom(second->next()->str()))) && (first->next()->str() == second->strAt(3))) { second = second->tokAt(2); } first = first->next(); second = second->next(); // skip "struct" if (first->str() == "struct" || first->str() == "enum") first = first->next(); if (second->str() == "struct" || second->str() == "enum") second = second->next(); // skip const on type passed by value if (Token::Match(first, "const %type% %name%|,|)") && !Token::Match(first, "const %type% %name%| [")) first = first->next(); if (Token::Match(second, "const %type% %name%|,|)") && !Token::Match(second, "const %type% %name%| [")) second = second->next(); } return false; } const Token * Function::constructorMemberInitialization() const { if (!isConstructor() || !functionScope || !functionScope->classStart) return nullptr; if (Token::Match(token, "%name% (") && Token::simpleMatch(token->linkAt(1), ") :")) return token->linkAt(1)->next(); return nullptr; } Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) { Function* function = nullptr; for (std::multimap::iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { if (Function::argsMatch(scope, i->second->argDef->next(), argStart->next(), 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; // save the function definition argument start '(' function.argDef = argStart; // save the access type function.access = Public; // save the function name location function.tokenDef = funcStart; function.isInline(false); function.hasBody(false); function.type = Function::eFunction; function.nestedIn = scope; const Token *tok1 = tok; // look for end of previous statement while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{")) tok1 = tok1->previous(); // find the return type while (Token::Match(tok1, "static|extern|const")) { if (tok1->str() == "static") function.isStaticLocal(true); else if (tok1->str() == "extern") function.isExtern(true); tok1 = tok1->next(); } if (function.argDef->link()->strAt(1) == ".") function.retDef = function.argDef->link()->tokAt(2); else if (tok1) function.retDef = tok1; 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) == "&&"); const Token *tok1; // skip class/struct name if (destructor) tok1 = (*tok)->tokAt(-3); else if ((*tok)->strAt(-2) == ">" && (*tok)->linkAt(-2)) tok1 = (*tok)->linkAt(-2)->previous(); else tok1 = (*tok)->tokAt(-2); // syntax error? if (!tok1) return; int count = 0; std::string path; unsigned int path_length = 0; // 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)))) { if (tok1->strAt(-2) == ">") { tok1 = tok1->tokAt(-2); const Token * tok2 = tok1->previous(); path = ":: " + path; if (tok2) { do { path = tok1->str() + " " + path; tok1 = tok1->previous(); count++; path_length++; } while (tok1 != tok2); } } else { path = tok1->str() + " :: " + path; tok1 = tok1->tokAt(-2); count++; path_length++; } } if (tok1 && count) { path = tok1->str() + " :: " + path; path_length++; } 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); if (func) { if (!func->hasBody()) { 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 > 0) { count--; tok1 = tok1->tokAt(2); scope2 = scope2->findInNestedList(tok1->str()); } if (count == 0 && 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? if ((*tok)->next()->link()) { const bool hasConstKeyword = (*tok)->next()->link()->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.push_back(Scope(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->classStart = tok1; newScope->classEnd = tok1->link(); // syntax error? if (!newScope->classEnd) { 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 = Public; tok2 = tok2->next(); } else if (tok2->str() == "protected") { base.access = Protected; tok2 = tok2->next(); } else if (tok2->str() == "private") { base.access = Private; tok2 = tok2->next(); } else { if (tok->str() == "class") base.access = Private; else if (tok->str() == "struct") base.access = 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->isName()) return next->str(); return emptyString; } void SymbolDatabase::debugMessage(const Token *tok, const std::string &msg) const { if (tok && _settings->debugwarnings) { const std::list locationList(1, tok); const ErrorLogger::ErrorMessage errmsg(locationList, &_tokenizer->list, Severity::debug, "debug", msg, false); if (_errorLogger) _errorLogger->reportErr(errmsg); } } const Function* Type::getFunction(const std::string& funcName) const { if (classScope) { std::multimap::const_iterator it = classScope->functionMap.find(funcName); if (it != classScope->functionMap.end()) return it->second; } for (std::size_t i = 0; i < derivedFrom.size(); i++) { if (derivedFrom[i].type) { const Function* const func = derivedFrom[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 Library* lib) { const Library::Container* container = lib->detectContainer(_start); if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) { const Token* tok = Token::findsimplematch(_start, "<"); if (tok) { Dimension dimension_; tok = tok->next(); for (int i = 0; i < container->size_templateArgNo && tok; i++) { tok = tok->nextTemplateArgument(); } if (tok) { dimension_.start = tok; dimension_.end = Token::findmatch(tok, ",|>"); if (dimension_.end) dimension_.end = dimension_.end->previous(); if (dimension_.start == dimension_.end) { dimension_.num = MathLib::toLongNumber(dimension_.start->str()); dimension_.known = true; } } assert((dimension_.start == nullptr) == (dimension_.end == nullptr)); _dimensions.push_back(dimension_); return true; } } const Token *dim = _name; if (!dim) { // Argument without name dim = _end; // 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_; // check for empty array dimension [] if (dim->next()->str() != "]") { dimension_.start = dim->next(); dimension_.end = dim->link()->previous(); if (dimension_.start == dimension_.end && dimension_.start->isNumber()) { dimension_.num = MathLib::toLongNumber(dimension_.start->str()); dimension_.known = true; } } assert((dimension_.start == nullptr) == (dimension_.end == nullptr)); _dimensions.push_back(dimension_); dim = dim->link()->next(); arr = true; } return arr; } void Variable::setFlags(const ValueType &valuetype) { if (valuetype.constness) setFlag(fIsConst,true); if (valuetype.pointer) setFlag(fIsPointer,true); } 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 Public: return "Public"; case Protected: return "Protected"; case Private: return "Private"; case Global: return "Global"; case Namespace: return "Namespace"; case Argument: return "Argument"; case Local: return "Local"; case 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 << "_name: " << tokenToString(var->nameToken(), _tokenizer) << std::endl; if (var->nameToken()) { std::cout << indent << " declarationId: " << var->declarationId() << std::endl; } std::cout << indent << "_start: " << tokenToString(var->typeStartToken(), _tokenizer) << std::endl; std::cout << indent << "_end: " << tokenToString(var->typeEndToken(), _tokenizer) << 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 << "_index: " << var->index() << std::endl; std::cout << indent << "_access: " << (var->isPublic() ? "Public" : var->isProtected() ? "Protected" : var->isPrivate() ? "Private" : var->isGlobal() ? "Global" : var->isNamespace() ? "Namespace" : var->isArgument() ? "Argument" : var->isLocal() ? "Local" : var->isThrow() ? "Throw" : "Unknown") << std::endl; std::cout << indent << "_flags: " << 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 << "_type: "; if (var->type()) { std::cout << var->type()->type() << " " << var->type()->name(); std::cout << " " << _tokenizer->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 << "_scope: " << scopeToString(var->scope(), _tokenizer) << std::endl; std::cout << indent << "_dimensions:"; 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, _tokenizer) << std::endl; std::cout << " classStart: " << tokenToString(scope->classStart, _tokenizer) << std::endl; std::cout << " classEnd: " << tokenToString(scope->classEnd, _tokenizer) << 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, _tokenizer) << 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" : "Unknown") << std::endl; std::cout << " access: " << (func->access == Public ? "Public" : func->access == Protected ? "Protected" : func->access == Private ? "Private" : "Unknown") << 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 << " isVirtual: " << func->isVirtual() << 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 << " 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 << " 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 "; 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, _tokenizer) << std::endl; std::cout << " argDef: " << tokenToString(func->argDef, _tokenizer) << std::endl; if (!func->isConstructor() && !func->isDestructor()) std::cout << " retDef: " << tokenToString(func->retDef, _tokenizer) << std::endl; if (func->retDef) { std::cout << " "; for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;"); 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, _tokenizer) << std::endl; std::cout << " arg: " << tokenToString(func->arg, _tokenizer) << std::endl; } std::cout << " nestedIn: " << scopeToString(func->nestedIn, _tokenizer) << std::endl; std::cout << " functionScope: " << scopeToString(func->functionScope, _tokenizer) << 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 << " " << _tokenizer->list.fileLine(use->start) << std::endl; } std::cout << " functionOf: " << scopeToString(scope->functionOf, _tokenizer) << 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, _tokenizer) << std::endl; std::cout << " classScope: " << type->classScope << std::endl; std::cout << " enclosingScope: " << type->enclosingScope << std::endl; std::cout << " needInitialization: " << (type->needInitialization == Type::Unknown ? "Unknown" : type->needInitialization == Type::True ? "True" : type->needInitialization == Type::False ? "False" : "Invalid") << std::endl; std::cout << " derivedFrom[" << type->derivedFrom.size() << "] = ("; std::size_t count = type->derivedFrom.size(); for (std::size_t i = 0; i < type->derivedFrom.size(); ++i) { if (type->derivedFrom[i].isVirtual) std::cout << "Virtual "; std::cout << (type->derivedFrom[i].access == Public ? " Public" : type->derivedFrom[i].access == Protected ? " Protected" : type->derivedFrom[i].access == Private ? " Private" : " Unknown"); if (type->derivedFrom[i].type) std::cout << " " << type->derivedFrom[i].type; else std::cout << " Unknown"; std::cout << " " << type->derivedFrom[i].name; if (count-- > 1) std::cout << ","; } std::cout << " )" << std::endl; std::cout << " friendList[" << type->friendList.size() << "] = ("; std::list::const_iterator fii; count = type->friendList.size(); for (fii = type->friendList.begin(); fii != type->friendList.end(); ++fii) { if (fii->type) std::cout << fii->type; else std::cout << " Unknown"; std::cout << " " << fii->name; if (count-- > 1) std::cout << ","; } std::cout << " )" << std::endl; } for (std::size_t i = 1; i < _variableList.size(); i++) { std::cout << "_variableList[" << i << "]: " << _variableList[i]; if (_variableList[i]) { std::cout << " " << _variableList[i]->name() << " " << _tokenizer->list.fileLine(_variableList[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); // 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->classStart) out << " classStart=\"" << scope->classStart << '\"'; if (scope->classEnd) out << " classEnd=\"" << scope->classEnd << '\"'; 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()) << '\"'; 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; } 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.. out << " " << std::endl; for (unsigned int i = 1U; i < _variableList.size(); i++) { const Variable *var = _variableList[i]; if (!var) continue; out << " nameToken() << '\"'; out << " typeStartToken=\"" << var->typeStartToken() << '\"'; out << " typeEndToken=\"" << var->typeEndToken() << '\"'; out << " isArgument=\"" << var->isArgument() << '\"'; out << " isArray=\"" << var->isArray() << '\"'; out << " isClass=\"" << var->isClass() << '\"'; out << " isExtern=\"" << var->isExtern() << '\"'; out << " isLocal=\"" << var->isLocal() << '\"'; out << " isPointer=\"" << var->isPointer() << '\"'; out << " isReference=\"" << var->isReference() << '\"'; out << " isStatic=\"" << var->isStatic() << '\"'; out << " access=\"" << accessControlToString(var->_access) << '\"'; 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 (std::list::const_iterator ui = scope->usingList.begin(); ui != scope->usingList.end(); ++ui) { 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 (!(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->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|enum|struct|::")) typeTok = typeTok->next(); // 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() && tok->strAt(-1) != "const") { 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")) startTok = startTok->next(); argumentList.push_back(Variable(nameTok, startTok, endTok, count++, Argument, argType, functionScope, &symbolDatabase->_settings->library)); 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 (isVirtual()) return true; else if (access == Private || access == Public || access == Protected) { bool safe = true; bool hasVirt = isImplicitlyVirtual_rec(nestedIn->definedType, safe); if (hasVirt) return true; else if (safe) return false; else return defaultVal; } else return false; } bool Function::isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const { // check each base class for (std::size_t i = 0; i < baseType->derivedFrom.size(); ++i) { const ::Type* derivedFromType = baseType->derivedFrom[i].type; // check if base class exists in database if (derivedFromType && derivedFromType->classScope) { 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->isVirtual()) { // Base is virtual and of same name const Token *temp1 = func->tokenDef->previous(); const Token *temp2 = tokenDef->previous(); bool returnMatch = true; // check for matching return parameters while (temp1->str() != "virtual") { if (temp1->str() != temp2->str() && !(temp1->str() == derivedFromType->name() && temp2->str() == baseType->name())) { returnMatch = false; break; } temp1 = temp1->previous(); temp2 = temp2->previous(); } // check for matching function parameters if (returnMatch && argsMatch(baseType->classScope, func->argDef, argDef, emptyString, 0)) { return true; } } } 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 hierarchie. if (isImplicitlyVirtual_rec(derivedFromType, safe)) { return true; } } } else { // unable to find base class so assume it has no virtual function safe = false; return false; } } return false; } const Variable* Function::getArgumentVar(std::size_t 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_), classStart(start_), classEnd(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_), classStart(nullptr), classEnd(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 { type = Scope::eFunction; } // skip over qualification if present if (nameTok && nameTok->str() == "::") nameTok = nameTok->next(); while (Token::Match(nameTok, "%type% ::")) nameTok = nameTok->tokAt(2); 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 Global; case eClass: return Private; case eStruct: return Public; case eUnion: return Public; case eNamespace: return Namespace; default: return Local; } } // Get variable list.. void Scope::getVariableList(const Library* lib) { const Token *start; if (classStart) start = classStart->next(); // global scope else if (className.empty()) start = check->_tokenizer->tokens(); // forward declaration else return; AccessControl varaccess = defaultAccess(); for (const Token *tok = start; tok && tok != classEnd; 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 = Public; continue; } else if (tok->str() == "protected:") { varaccess = Protected; continue; } else if (tok->str() == "private:") { varaccess = 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; } // 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, lib); if (!tok) break; } } const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, const Library* lib) { // Is it a throw..? if (Token::Match(tok, "throw %any% (") && Token::simpleMatch(tok->linkAt(2), ") ;")) { return tok->linkAt(2); } else 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|static|mutable|extern while (Token::Match(tok, "const|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, lib); } 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* skipScopeIdentifiers(const Token* tok) { if (tok && tok->str() == "::") { tok = tok->next(); } while (Token::Match(tok, "%type% ::")) { tok = tok->tokAt(2); } return tok; } 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; } bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const { const bool isCPP = check && check->_tokenizer->isCPP(); if (isCPP && Token::Match(tok, "throw|new")) return false; const bool isCPP11 = isCPP && check->_settings->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 = skipPointers(localTypeTok->strAt(1)=="const"?localTypeTok->tokAt(2):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 (tokensThatAreNotEnumeratorValues.find(tokStr) != tokensThatAreNotEnumeratorValues.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 (size_t i = 0, end = derivedFrom.size(); i < end; ++i) { const Type *derivedFromType = derivedFrom[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; } } } tokensThatAreNotEnumeratorValues.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 (std::size_t i = 0; i < derivedFrom.size(); ++i) { const Type *base = derivedFrom[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) != "::") { 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% ::")) tok1 = tok1->tokAt(-2); // 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 scope = scope->nestedIn; } } } if (scope) { // follow qualification while (scope && Token::Match(tok1, "%type% ::")) { tok1 = tok1->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 (std::list::const_iterator it = nestedList.begin(); it != nestedList.end(); ++it) { const Scope *s = *it; // Inline function if (s->type == Scope::eUnconditional && Token::simpleMatch(s->classStart->previous(), ") {")) return true; // Lambda function if (s->type == Scope::eLambda) return true; } return false; } void Scope::findFunctionInBase(const std::string & name, size_t args, std::vector & matches) const { if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector &derivedFrom = definedType->derivedFrom; for (std::size_t i = 0; i < derivedFrom.size(); ++i) { const Type *base = derivedFrom[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) { 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) { bool takesInt = Token::Match(funcarg->typeStartToken(), "char|short|int|long"); bool takesFloat = Token::Match(funcarg->typeStartToken(), "float|double"); bool passesInt = Token::Match(callarg->typeStartToken(), "char|short|int|long"); bool passesFloat = Token::Match(callarg->typeStartToken(), "float|double"); if ((takesInt && passesInt) || (takesFloat && passesFloat)) fallback1++; else if ((takesInt && passesFloat) || (takesFloat && passesInt)) fallback2++; } } } 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; std::vector arguments; // find all the arguments for this function call for (const Token *arg = tok->tokAt(2); arg && arg != end; arg = arg->nextArgument()) { arguments.push_back(arg); } std::vector matches; // find all the possible functions that could match const std::size_t args = arguments.size(); for (std::multimap::const_iterator it = functionMap.find(tok->str()); it != 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); } } // 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()) { // 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); // check for a match with a variable if (Token::Match(arguments[j], "%var% ,|)")) { const Variable * callarg = check->getVariableFromVarId(arguments[j]->varId()); checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2); } // check for a match with address of a variable else if (Token::Match(arguments[j], "& %var% ,|)")) { const Variable * callarg = check->getVariableFromVarId(arguments[j]->next()->varId()); if (callarg) { bool funcargptr = (funcarg->typeEndToken()->str() == "*"); if (funcargptr && (callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong())) { same++; } else if (funcargptr && funcarg->typeStartToken()->str() == "void") { fallback1++; } else { // can't match so remove this function from possible matches matches.erase(matches.begin() + i); erased = true; break; } } } // check for a match with a numeric literal else if (Token::Match(arguments[j], "%num% ,|)")) { if (MathLib::isInt(arguments[j]->str()) && (!funcarg->isPointer() || MathLib::isNullValue(arguments[j]->str()))) { bool exactMatch = false; if (arguments[j]->str().find("ll") != std::string::npos || arguments[j]->str().find("LL") != std::string::npos) { if (arguments[j]->str().find('u') != std::string::npos || arguments[j]->str().find('U') != std::string::npos) { if (funcarg->typeStartToken()->isLong() && funcarg->typeStartToken()->isUnsigned() && funcarg->typeStartToken()->str() == "long") { exactMatch = true; } } else { if (funcarg->typeStartToken()->isLong() && !funcarg->typeStartToken()->isUnsigned() && funcarg->typeStartToken()->str() == "long") { exactMatch = true; } } } else if (arguments[j]->str().find('l') != std::string::npos || arguments[j]->str().find('L') != std::string::npos) { if (arguments[j]->str().find('u') != std::string::npos || arguments[j]->str().find('U') != std::string::npos) { if (!funcarg->typeStartToken()->isLong() && funcarg->typeStartToken()->isUnsigned() && funcarg->typeStartToken()->str() == "long") { exactMatch = true; } } else { if (!funcarg->typeStartToken()->isLong() && !funcarg->typeStartToken()->isUnsigned() && funcarg->typeStartToken()->str() == "long") { exactMatch = true; } } } else if (arguments[j]->str().find('u') != std::string::npos || arguments[j]->str().find('U') != std::string::npos) { if (funcarg->typeStartToken()->isUnsigned() && funcarg->typeStartToken()->str() == "int") { exactMatch = true; } else if (Token::Match(funcarg->typeStartToken(), "char|short")) { exactMatch = true; } } else { if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) { exactMatch = true; } } if (exactMatch) if (funcarg->isPointer()) fallback2++; else same++; else { if (funcarg->isPointer() || Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) fallback1++; else if (Token::Match(funcarg->typeStartToken(), "float|double")) fallback2++; } } else if (!funcarg->isPointer()) { bool exactMatch = false; if (arguments[j]->str().find('f') != std::string::npos || arguments[j]->str().find('F') != std::string::npos) { if (funcarg->typeStartToken()->str() == "float") { exactMatch = true; } } else if (arguments[j]->str().find('l') != std::string::npos || arguments[j]->str().find('L') != std::string::npos) { if (funcarg->typeStartToken()->isLong() && funcarg->typeStartToken()->str() == "double") { exactMatch = true; } } else { if (!funcarg->typeStartToken()->isLong() && funcarg->typeStartToken()->str() == "double") { exactMatch = true; } } if (exactMatch) same++; else { if (Token::Match(funcarg->typeStartToken(), "float|double")) fallback1++; else if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) fallback2++; } } } // check for a match with a string literal else if (Token::Match(arguments[j], "%str% ,|)")) { if (funcarg->typeStartToken() != funcarg->typeEndToken() && ((!arguments[j]->isLong() && Token::simpleMatch(funcarg->typeStartToken(), "char *")) || (arguments[j]->isLong() && Token::simpleMatch(funcarg->typeStartToken(), "wchar_t *")))) same++; else if (Token::simpleMatch(funcarg->typeStartToken(), "void *")) fallback1++; else if (funcarg->isStlStringType()) fallback2++; } // check for a match with a char literal else if (!funcarg->isArrayOrPointer() && Token::Match(arguments[j], "%char% ,|)")) { if (arguments[j]->isLong() && funcarg->typeStartToken()->str() == "wchar_t") same++; else if (!arguments[j]->isLong() && funcarg->typeStartToken()->str() == "char") same++; else if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) fallback1++; } // check for a match with a boolean literal else if (!funcarg->isArrayOrPointer() && Token::Match(arguments[j], "%bool% ,|)")) { if (Token::Match(funcarg->typeStartToken(), "bool|_Bool")) same++; else if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) fallback1++; } // check for a match with nullptr else if (funcarg->isPointer() && Token::Match(arguments[j], "nullptr|NULL ,|)")) { same++; } // check that function argument type is not mismatching else if (funcarg->isReference() && arguments[j]->str() == "&") { // can't match so remove this function from possible matches matches.erase(matches.begin() + i); erased = true; break; } // Try to evaluate the apparently more complex expression else { const Token* argtok = arguments[j]; while (argtok->astParent() && argtok->astParent() != tok->next() && argtok->astParent()->str() != ",") { argtok = argtok->astParent(); } if (argtok && argtok->valueType() && !funcarg->isArrayOrPointer()) { // TODO: Pointers if (argtok->valueType()->type == ValueType::BOOL) { if (funcarg->typeStartToken()->str() == "bool") same++; else if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) fallback1++; } else if (argtok->valueType()->isIntegral()) { if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) same++; else if (Token::Match(funcarg->typeStartToken(), "float|double")) fallback1++; } else if (argtok->valueType()->isFloat()) { if (Token::Match(funcarg->typeStartToken(), "float|double")) same++; else if (Token::Match(funcarg->typeStartToken(), "wchar_t|char|short|int|long")) fallback1++; } } else { while (Token::Match(argtok, ".|::")) argtok = argtok->astOperand2(); if (argtok) { const Variable * callarg = check->getVariableFromVarId(argtok->varId()); checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2); } } } } size_t hasToBe = func->isVariadic() ? (func->argCount() - 1) : args; // check if all arguments matched if (same == hasToBe) { if (constFallback) 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% ::")) tok1 = tok1->tokAt(-2); // check for global scope if (tok1->strAt(-1) == "::") { currScope = &scopeList.front(); 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% (")) { tok1 = tok1->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->isConst()); } } // 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); } return nullptr; } //--------------------------------------------------------------------------- const Type* Scope::findType(const std::string & name) const { auto it = definedTypesMap.find(name); if (definedTypesMap.end() == it) { return nullptr; } else { return (*it).second; } } //--------------------------------------------------------------------------- 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 _tokenizer->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 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) == "::") { scope = scope->findRecordInNestedList(tok->str()); if (scope) { tok = tok->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) == "::") { scope = scope->findRecordInNestedList(tok->str()); if (scope) { tok = tok->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) == "::") { hasPath = true; scope = scope->findRecordInNestedList(tok->str()); if (scope) { tok = tok->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 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, func->tokAt(2), it->second->argDef->next(), emptyString, 0) && it->second->isDestructor() == destructor) { function = it->second; break; } } if (!function) { const Scope * scope = ns->findRecordInNestedList(func->str()); if (scope && func->strAt(1) == "::") { func = func->tokAt(2); if (func->str() == "~") func = func->next(); function = findFunctionInScope(func, scope); } } return const_cast(function); } //--------------------------------------------------------------------------- namespace { const std::set c_keywords = make_container< std::set >() << "_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 cpp_keywords = make_container< std::set >() << c_keywords << "alignas" << "alignof" << "and" << "and_eq" << "asm" << "auto" << "bitand" << "bitor" << "bool" << "break" << "case" << "catch" << "char" << "class" << "compl" << "concept" << "const" << "constexpr" << "const_cast" << "continue" << "decltype" << "default" << "delete" << "do" << "double" << "dynamic_cast" << "else" << "enum" << "explicit" << "export" << "extern" << "false" << "float" << "for" << "friend" << "goto" << "if" << "inline" << "int" << "long" << "mutable" << "namespace" << "new" << "noexcept" << "not" << "not_eq" << "nullptr" << "operator" << "or" << "or_eq" << "private" << "protected" << "public" << "register" << "reinterpret_cast" << "requires" << "return" << "short" << "signed" << "sizeof" << "static" << "static_assert" << "static_cast" << "struct" << "switch" << "template" << "this" << "thread_local" << "throw" << "true" << "try" << "typedef" << "typeid" << "typename" << "union" << "unsigned" << "using" << "virtual" << "void" << "volatile" << "wchar_t" << "while" << "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(); } unsigned int SymbolDatabase::sizeOfType(const Token *type) const { unsigned int size = _tokenizer->sizeOfType(type); if (size == 0 && type->type() && type->type()->isEnumType() && type->type()->classScope) { size = _settings->sizeof_int; const Token * enum_type = type->type()->classScope->enumType; if (enum_type) size = _tokenizer->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; valuetype.pointer = var.dimensions().size(); valuetype.typeScope = var.typeScope(); if (parsedecl(var.typeStartToken(), &valuetype, defaultSignedness, _settings)) 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(), _settings); 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 = defaultSignedness; 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 = const_cast(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 (!cpp || (vt2 && vt2->isIntegral())) setValueType(parent, *vt1); return; } if (parent->isAssignmentOp()) { if (vt1) setValueType(parent, *vt1); else if (cpp && ((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 (autoTok->strAt(1) == "*" && vt.pointer) vt.pointer--; if (autoTok->strAt(-1) == "const") vt.constness |= 1; setValueType(autoTok, vt); setAutoTokenProperties(autoTok); setValueType(var1Tok, *vt2); setValueType(parent->previous(), *vt2); Variable *var = const_cast(parent->previous()->variable()); if (var) { var->setFlags(*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() == "[" && (!cpp || 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; } if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; 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 = const_cast(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->setFlags(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; } // TODO: Get type better if (Token::Match(typeStart, "std :: %type% < %type% *| *| >")) { ValueType autovt; if (parsedecl(typeStart->tokAt(4), &autovt, defaultSignedness, _settings)) { 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->setFlags(varvt); const Type * type = typeStart->tokAt(4)->type(); if (type && type->classScope && type->classScope->definedType) { autoToken->type(type->classScope->definedType); var->type(type->classScope->definedType); } } } } } } if (!vt1) return; if (parent->astOperand2() && !vt2) return; 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 = const_cast(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 (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) { 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 unsigned int pointer0 = valuetype->pointer; while (Token::Match(type->previous(), "%name%")) type = type->previous(); valuetype->sign = ValueType::Sign::UNKNOWN_SIGN; if (!valuetype->typeScope) valuetype->type = ValueType::Type::UNKNOWN_TYPE; 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; 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%|*|&|::") && !type->variable()) { 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()) 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()) type = type->link(); type = type->next(); } continue; } else if (Token::Match(type, "%name% :: %name%")) { std::string typestr; const Token *end = type; while (Token::Match(end, "%name% :: %name%")) { typestr += end->str() + "::"; end = end->tokAt(2); } typestr += end->str(); if (valuetype->fromLibraryType(typestr, settings)) type = end; } else if (ValueType::Type::UNKNOWN_TYPE != ValueType::typeFromString(type->str(), type->isLong())) valuetype->type = 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; 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); 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; 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() { Token * tokens = const_cast(_tokenizer)->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().back(); 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())) { 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 (_settings->platformType == cppcheck::Platform::Unspecified) type = ValueType::Type::INT; else if (_settings->isIntValue(unsignedSuffix ? (value >> 1) : value)) type = ValueType::Type::INT; else if (_settings->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 (cpp && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) { const Function *function = getOperatorFunction(tok); if (function) { ValueType vt; parsedecl(function->retDef, &vt, defaultSignedness, _settings); setValueType(tok, vt); continue; } } setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); } else if (tok->tokType() == Token::eChar) setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 0U)); else if (tok->tokType() == Token::eString) { ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 1U, 1U); if (tok->isLong()) { valuetype.originalTypeName = "wchar_t"; valuetype.type = ValueType::Type::SHORT; } setValueType(tok, valuetype); } else if (tok->str() == "(") { // cast if (!tok->astOperand2() && Token::Match(tok, "( %name%")) { ValueType valuetype; if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, defaultSignedness, _settings), ")")) setValueType(tok, valuetype); } // C++ cast 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, defaultSignedness, _settings), ">")) setValueType(tok, valuetype); } // function else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) { ValueType valuetype; if (parsedecl(tok->previous()->function()->retDef, &valuetype, defaultSignedness, _settings)) 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, defaultSignedness, _settings)) { setValueType(tok->next(), vt); } } } // library function else if (tok->previous()) { const std::string& typestr(_settings->library.returnValueType(tok->previous())); 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; 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(_settings); std::istringstream istr(typestr+";"); if (tokenList.createTokens(istr)) { ValueType vt; assert(tokenList.front()); tokenList.simplifyStdType(); if (parsedecl(tokenList.front(), &vt, defaultSignedness, _settings)) { setValueType(tok, vt); } } } } else if (tok->variable()) { setValueType(tok, *tok->variable()); } else if (tok->enumerator()) { setValueType(tok, *tok->enumerator()); } else if (cpp && tok->str() == "new") { const Token *typeTok = tok->next(); if (Token::Match(typeTok, "( std| ::| nothrow )")) typeTok = typeTok->link()->next(); if (const Library::Container *c = _settings->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, _settings); 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) ? defaultSignedness : ValueType::Sign::SIGNED; } setValueType(tok, vt); } } // 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 == "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 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->_type == "char") type = ValueType::Type::CHAR; else if (platformType->_type == "short") type = ValueType::Type::SHORT; else if (platformType->_type == "int") type = platformType->_long ? ValueType::Type::LONG : ValueType::Type::INT; else if (platformType->_type == "long") type = platformType->_long ? ValueType::Type::LONGLONG : ValueType::Type::LONG; if (platformType->_signed) sign = ValueType::SIGNED; else if (platformType->_unsigned) sign = ValueType::UNSIGNED; if (platformType->_pointer) pointer = 1; if (platformType->_ptr_ptr) pointer = 2; if (platformType->_const_ptr) 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 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 (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(); } 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 == 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->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->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 + ')'; } for (unsigned int p = 0; p < pointer; p++) { ret += " *"; if (constness & (2 << p)) ret += " const"; } return ret.empty() ? ret : ret.substr(1); } cppcheck-1.82/lib/symboldatabase.h000066400000000000000000001122511322667425100171470ustar00rootroot00000000000000/* * 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 symboldatabaseH #define symboldatabaseH //--------------------------------------------------------------------------- #include "config.h" #include "library.h" #include "mathlib.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 AccessControl { Public, Protected, Private, Global, Namespace, Argument, Local, Throw }; /** * @brief Array dimension information. */ struct Dimension { Dimension() : start(nullptr), end(nullptr), num(0), known(true) { } const Token *start; // size start token const Token *end; // size end 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 NeedInitialization { Unknown, True, False } needInitialization; class BaseInfo { public: BaseInfo() : type(nullptr), nameTok(nullptr), access(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; std::string name; const Type* type; }; std::vector derivedFrom; std::list 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(Unknown), typeStart(nullptr), typeEnd(nullptr) { if (classDef_ && classDef_->str() == "enum") 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 hierarchie * @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 */ }; /** * 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 ((_flags & 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_) { _flags = state_ ? _flags | flag_ : _flags & ~flag_; } /** * @brief parse and save array dimension information * @param lib Library instance * @return true if array, false if not */ bool arrayDimensions(const Library* lib); public: Variable(const Token *name_, const Token *start_, const Token *end_, std::size_t index_, AccessControl access_, const Type *type_, const Scope *scope_, const Library* lib) : _name(name_), _start(start_), _end(end_), _index(index_), _access(access_), _flags(0), _type(type_), _scope(scope_) { evaluate(lib); } /** * Get name token. * @return name token */ const Token *nameToken() const { return _name; } /** * 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 _start; } /** * 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 _end; } /** * 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 (_name) return _name->str(); return emptyString; } /** * Get declaration ID (varId used for variable in its declaration). * @return declaration ID */ unsigned int declarationId() const { // name may not exist for function arguments if (_name) return _name->varId(); return 0; } /** * Get index of variable in declared order. * @return variable index */ std::size_t index() const { return _index; } /** * Is variable public. * @return true if public, false if not */ bool isPublic() const { return _access == Public; } /** * Is variable protected. * @return true if protected, false if not */ bool isProtected() const { return _access == Protected; } /** * Is variable private. * @return true if private, false if not */ bool isPrivate() const { return _access == Private; } /** * Is variable global. * @return true if global, false if not */ bool isGlobal() const { return _access == Global; } /** * Is variable in a namespace. * @return true if in a namespace, false if not */ bool isNamespace() const { return _access == Namespace; } /** * Is variable a function argument. * @return true if a function argument, false if not */ bool isArgument() const { return _access == Argument; } /** * Is variable local. * @return true if local, false if not */ bool isLocal() const { return (_access == 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 _access == 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 _type; } /** * Get Scope pointer of known type. * @return pointer to type scope if known, NULL if not known */ const Scope *typeScope() const { return _type ? _type->classScope : nullptr; } /** * Get Scope pointer of enclosing scope. * @return pointer to enclosing scope */ const Scope *scope() const { return _scope; } /** * Get array dimensions. * @return array dimensions vector */ const std::vector &dimensions() const { return _dimensions; } /** * Get array dimension length. * @return length of dimension */ MathLib::bigint dimension(std::size_t index_) const { return _dimensions[index_].num; } /** * Get array dimension known. * @return length of dimension known */ bool dimensionKnown(std::size_t index_) const { return _dimensions[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); } /** * 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==_start->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(_start->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(); } void setFlags(const ValueType &valuetype); 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) { _type = t; } /** @brief variable name token */ const Token *_name; /** @brief variable type start token */ const Token *_start; /** @brief variable type end token */ const Token *_end; /** @brief order declared */ std::size_t _index; /** @brief what section is this variable declared in? */ AccessControl _access; // public/protected/private /** @brief flags */ unsigned int _flags; /** @brief pointer to user defined type info (for known types) */ const Type *_type; /** @brief pointer to scope this variable is in */ const Scope *_scope; /** @brief array dimensions */ std::vector _dimensions; /** @brief fill in information, depending on Tokens given at instantiation */ void evaluate(const Library* lib); }; class CPPCHECKLIB Function { /** @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 */ fIsVirtual = (1 << 3), /** @brief is virtual */ 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 */ fIsNoExcept = (1 << 12), /** @brief is noexcept */ fIsThrow = (1 << 13), /** @brief is throw */ fIsOperator = (1 << 14), /** @brief is operator */ fHasLvalRefQual = (1 << 15), /** @brief has & lvalue ref-qualifier */ fHasRvalRefQual = (1 << 16), /** @brief has && rvalue ref-qualifier */ fIsVariadic = (1 << 17) /** @brief is variadic */ }; /** * 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 ((flags & 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) { flags = state ? flags | flag : flags & ~flag; } public: enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction }; Function() : tokenDef(nullptr), argDef(nullptr), token(nullptr), arg(nullptr), retDef(nullptr), retType(nullptr), functionScope(nullptr), nestedIn(nullptr), initArgCount(0), type(eFunction), access(Public), noexceptArg(nullptr), throwArg(nullptr), flags(0) { } const std::string &name() const { return tokenDef->str(); } std::size_t argCount() const { return argumentList.size(); } std::size_t minArgCount() const { return argumentList.size() - initArgCount; } const Variable* getArgumentVar(std::size_t num) const; unsigned 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; 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 hasBody() const { return getFlag(fHasBody); } bool isInline() const { return getFlag(fIsInline); } bool isConst() const { return getFlag(fIsConst); } bool isVirtual() const { return getFlag(fIsVirtual); } 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 isOperator() const { return getFlag(fIsOperator); } bool hasLvalRefQualifier() const { return getFlag(fHasLvalRefQual); } bool hasRvalRefQualifier() const { return getFlag(fHasRvalRefQual); } bool isVariadic() const { return getFlag(fIsVariadic); } void hasBody(bool state) { setFlag(fHasBody, state); } void isInline(bool state) { setFlag(fIsInline, state); } void isConst(bool state) { setFlag(fIsConst, state); } void isVirtual(bool state) { setFlag(fIsVirtual, 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); } 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 unsigned int initArgCount; // number of args with default values Type type; // constructor, destructor, ... AccessControl access; // public/protected/private const Token *noexceptArg; const Token *throwArg; static bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, unsigned int depth); /** * @return token to ":" if the function is a constructor * and it contains member initialization otherwise a nullptr is returned */ const Token * constructorMemberInitialization() const; private: bool isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const; unsigned int flags; }; 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 *classStart; // '{' token const Token *classEnd; // '}' token std::list functionList; std::multimap functionMap; std::list varlist; const Scope *nestedIn; std::list nestedList; unsigned int numConstructors; unsigned 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 (std::size_t i = 0, end = enumeratorList.size(); i < end; ++i) { if (enumeratorList[i].name->str() == name) return &enumeratorList[i]; } return nullptr; } 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 Library* lib) { varlist.push_back(Variable(token_, start_, end_, varlist.size(), access_, type_, scope_, lib)); } /** @brief initialize varlist */ void getVariableList(const Library* lib); 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 lib Library instance * @return pointer to last token */ const Token *checkVariable(const Token *tok, AccessControl varaccess, const Library* lib); /** * @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, size_t args, std::vector & matches) const; }; /** Value type */ class CPPCHECKLIB ValueType { private: // No assignment ValueType &operator=(const ValueType &other); public: enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign; enum Type { UNKNOWN_TYPE, NONSTD, RECORD, CONTAINER, ITERATOR, VOID, BOOL, CHAR, SHORT, INT, LONG, LONGLONG, UNKNOWN_INT, FLOAT, DOUBLE, LONGDOUBLE } type; unsigned int pointer; // 0=>not pointer, 1=>*, 2=>**, 3=>***, etc unsigned int constness; // bit 0=data, bit 1=*, bit 2=** const Scope *typeScope; const Library::Container *container; std::string originalTypeName; ValueType() : sign(UNKNOWN_SIGN), type(UNKNOWN_TYPE), pointer(0U), constness(0U), typeScope(nullptr), container(nullptr) {} ValueType(const ValueType &vt) : sign(vt.sign), type(vt.type), pointer(vt.pointer), constness(vt.constness), typeScope(vt.typeScope), container(vt.container), originalTypeName(vt.originalTypeName) {} ValueType(enum Sign s, enum Type t, unsigned int p) : sign(s), type(t), pointer(p), constness(0U), typeScope(nullptr), container(nullptr) {} ValueType(enum Sign s, enum Type t, unsigned int p, unsigned int c) : sign(s), type(t), pointer(p), constness(c), typeScope(nullptr), container(nullptr) {} ValueType(enum Sign s, enum Type t, unsigned int p, unsigned int c, const std::string &otn) : sign(s), type(t), pointer(p), constness(c), typeScope(nullptr), container(nullptr), originalTypeName(otn) {} static ValueType parseDecl(const Token *type, const Settings *settings); static Type typeFromString(const std::string &typestr, bool longType); 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); std::string str() const; std::string dump() const; }; class CPPCHECKLIB SymbolDatabase { 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(std::size_t varId) const { return _variableList.at(varId); } std::size_t getVariableListSize() const { return _variableList.size(); } /** * @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(); /** * 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. */ unsigned int sizeOfType(const Token *type) const; 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 createSymbolDatabaseUnknownArrayDimensions(); 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 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 *_tokenizer; const Settings *_settings; ErrorLogger *_errorLogger; /** variable symbol table */ std::vector _variableList; /** list for missing types */ std::list _blankTypes; bool cpp; ValueType::Sign defaultSignedness; /** "negative cache" list of tokens that we find are not enumeration values */ mutable std::set tokensThatAreNotEnumeratorValues; }; //--------------------------------------------------------------------------- #endif // symboldatabaseH cppcheck-1.82/lib/templatesimplifier.cpp000066400000000000000000002162721322667425100204170ustar00rootroot00000000000000/* * 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 "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 namespace { class FindToken { public: explicit FindToken(const Token *t) : token(t) {} bool operator()(const TemplateSimplifier::TokenAndName &t) const { return t.token == token; } private: const Token * const token; }; class FindName { public: explicit FindName(const std::string &s) : name(s) {} bool operator()(const TemplateSimplifier::TokenAndName &t) const { return t.name == name; } private: const std::string name; }; } void TemplateSimplifier::cleanupAfterSimplify(Token *tokens) { bool goback = false; for (Token *tok = tokens; 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 == tokens) goback = true; } } } } void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates(const Token *tokens) { // check for more complicated syntax errors when using templates.. for (const Token *tok = tokens; 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::simpleMatch(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::Match(tok2, "[;{}]"); tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == "<") { bool inclevel = false; if (Token::simpleMatch(tok2->previous(), "operator <")) ; else if (level == 0) 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; } 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; tok = tok->next(); unsigned int level = 0; while (tok) { // 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::Match(tok, "%type% . . .")) { tok = tok->tokAt(4); continue; } // Skip '=', '?', ':' if (Token::Match(tok, "=|?|:")) tok = tok->next(); if (!tok) return 0; // Skip casts if (tok->str() == "(") { tok = tok->link(); if (tok) tok = tok->next(); } // 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() == ">") ? numberOfParameters : 0); --level; if (tok->str() == ">>") { if (level == 0) return numberOfParameters; --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; } 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) Token::eraseTokens(tok,tok2); tok->deleteThis(); return false; } else if (tok2->str() == "{") { tok2 = tok2->link()->next(); Token::eraseTokens(tok, tok2); if (tok2 && tok2->str() == ";" && tok2->next()) tok->deleteNext(); tok->deleteThis(); return true; } else if (tok2->str() == "}") { // garbage code! (#3449) Token::eraseTokens(tok,tok2); tok->deleteThis(); 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)))) { Token::eraseTokens(tok, tok2); tok->deleteThis(); return true; } if (tok2->str() == ";") { tok2 = tok2->next(); Token::eraseTokens(tok, tok2); tok->deleteThis(); return true; } if (tok2->str() == "<") ++indentlevel; else if (indentlevel >= 2 && tok2->str() == ">") --indentlevel; else if (Token::Match(tok2, "> class|struct %name% [,)]")) { tok2 = tok2->next(); Token::eraseTokens(tok, tok2); tok->deleteThis(); return true; } } return false; } std::set TemplateSimplifier::expandSpecialized(Token *tokens) { std::set expandedtemplates; // Locate specialized templates.. for (Token *tok = tokens; tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "template < >")) continue; // what kind of template is this? Token *tok2 = tok->tokAt(3); while (tok2 && (tok2->isName() || tok2->str() == "*")) tok2 = tok2->next(); if (!TemplateSimplifier::templateParameters(tok2)) continue; // unknown template.. bail out if (!tok2->previous()->isName()) continue; tok2 = tok2->previous(); std::string s; { std::ostringstream ostr; const Token *tok3 = tok2; for (; tok3 && tok3->str() != ">"; tok3 = tok3->next()) { if (tok3 != tok2) ostr << " "; ostr << tok3->str(); } if (!Token::Match(tok3, "> (|{|:")) continue; s = ostr.str(); } // remove spaces to create new name const std::string name(s + " >"); expandedtemplates.insert(name); // Rename template.. Token::eraseTokens(tok2, Token::findsimplematch(tok2, "<")->findClosingBracket()->next()); tok2->str(name); // delete the "template < >" tok->deleteNext(2); tok->deleteThis(); // Use this special template in the code.. while (nullptr != (tok2 = const_cast(Token::findsimplematch(tok2, name.c_str())))) { Token::eraseTokens(tok2, Token::findsimplematch(tok2, "<")->findClosingBracket()->next()); tok2->str(name); } } return expandedtemplates; } /// TODO: This is copy pasted from Tokenizer. We should reuse this code. namespace { struct ScopeInfo2 { ScopeInfo2(const std::string &name_, const Token *classEnd_) : name(name_), classEnd(classEnd_) {} const std::string name; const Token * const classEnd; }; } static std::string getScopeName(const std::list &scopeInfo) { std::string ret; for (std::list::const_iterator it = scopeInfo.begin(); it != scopeInfo.end(); ++it) ret += (ret.empty() ? "" : " :: ") + (it->name); return ret; } static std::string getFullName(const std::list &scopeInfo, const std::string &name) { const std::string &scopeName = getScopeName(scopeInfo); return scopeName + (scopeName.empty() ? "" : " :: ") + name; } static void setScopeInfo(const Token *tok, std::list *scopeInfo) { while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().classEnd) scopeInfo->pop_back(); if (!Token::Match(tok, "namespace|class|struct %name% {|:|::")) 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() == ":") { // ... } if (tok && tok->str() == "{") { scopeInfo->push_back(ScopeInfo2(classname,tok->link())); } } std::list TemplateSimplifier::getTemplateDeclarations(Token *tokens, bool &codeWithTemplates) { std::list scopeInfo; std::list declarations; for (Token *tok = tokens; tok; tok = tok->next()) { setScopeInfo(tok, &scopeInfo); if (!Token::simpleMatch(tok, "template <")) continue; // 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; Token *parmEnd = tok->next()->findClosingBracket(); for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; // Just a declaration => ignore this else if (tok2->str() == ";") break; // Implementation => add to "templates" else if (tok2->str() == "{") { int namepos = getTemplateNamePosition(parmEnd); if (namepos > 0) declarations.push_back(TokenAndName(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos)))); break; } } } return declarations; } std::list TemplateSimplifier::getTemplateInstantiations(Token *tokens, const std::list &declarations) { std::list instantiations; std::list scopeList; for (Token *tok = tokens; tok; tok = tok->next()) { setScopeInfo(tok, &scopeList); // template definition.. skip it if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (!tok) break; // #7914 // Ignore template instantiations within template definitions: they will only be // handled if the definition is actually instantiated const Token *tok2 = Token::findmatch(tok, "{|;"); if (tok2 && tok2->str() == "{") tok = tok2->link(); } else if (Token::Match(tok->previous(), "[({};=] %name% ::|<") || Token::Match(tok->previous(), "%type% %name% ::|<") || Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { std::string scopeName = getScopeName(scopeList); while (Token::Match(tok, "%name% :: %name%")) { scopeName += (scopeName.empty() ? "" : " :: ") + tok->str(); tok = tok->tokAt(2); } if (!Token::Match(tok, "%name% <")) continue; // Add inner template instantiations first => go to the ">" // and then parse backwards, adding all seen instantiations const Token *tok2 = tok->next()->findClosingBracket(); // parse backwards and add template instantiations // TODO for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { if (Token::Match(tok2, ", %name% <") && TemplateSimplifier::templateParameters(tok2->tokAt(2))) { instantiations.push_back(TokenAndName(tok2->next(), getScopeName(scopeList), getFullName(scopeList, tok2->strAt(1)))); } } // Add outer template.. if (TemplateSimplifier::templateParameters(tok->next())) { const std::string scopeName1(scopeName); while (true) { const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + tok->str(); const std::list::const_iterator it = std::find_if(declarations.begin(), declarations.end(), FindName(fullName)); if (it != declarations.end()) { instantiations.push_back(TokenAndName(tok, getScopeName(scopeList), fullName)); break; } else { if (scopeName.empty()) { instantiations.push_back(TokenAndName(tok, getScopeName(scopeList), scopeName1 + (scopeName1.empty()?"":" :: ") + tok->str())); break; } const std::string::size_type pos = scopeName.rfind(" :: "); scopeName = (pos == std::string::npos) ? std::string() : scopeName.substr(0,pos); } } } } } return instantiations; } void TemplateSimplifier::useDefaultArgumentValues(const std::list &templates, std::list * const templateInstantiations) { for (std::list::const_iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) { // 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; // the template classname. This will be empty for template functions std::string classname; // Scan template declaration.. for (Token *tok = iter1->token; tok; tok = tok->next()) { if (Token::simpleMatch(tok, "template < >")) { // Ticket #5762: Skip specialization tokens tok = tok->tokAt(2); if (0 == templateParmDepth) break; continue; } if (tok->str() == "(") { // Ticket #6835 tok = tok->link(); continue; } if (tok->str() == "<" && templateParameters(tok)) ++templateParmDepth; // end of template parameters? if (tok->str() == ">") { if (Token::Match(tok, "> class|struct %name%")) classname = tok->strAt(2); if (templateParmDepth<2) break; else --templateParmDepth; } // 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() || classname.empty()) continue; // iterate through all template instantiations for (std::list::const_iterator iter2 = templateInstantiations->begin(); iter2 != templateInstantiations->end(); ++iter2) { Token *tok = iter2->token; if (!Token::simpleMatch(tok, (classname + " <").c_str())) continue; // count the parameters.. tok = tok->next(); const unsigned int usedpar = TemplateSimplifier::templateParameters(tok); tok = tok->findClosingBracket(); 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; while (it != eq.end()) { int indentlevel = 0; tok->insertToken(","); tok = tok->next(); const Token *from = (*it)->next(); std::stack links; while (from && (!links.empty() || indentlevel || !Token::Match(from, ",|>"))) { if (from->str() == "<") ++indentlevel; else if (from->str() == ">") --indentlevel; 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; } } } for (std::list::iterator it = eq.begin(); it != eq.end(); ++it) { Token * const eqtok = *it; 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% <") && templateParameters(tok2->next())) { std::list::iterator ti = std::find_if(templateInstantiations->begin(), templateInstantiations->end(), FindToken(tok2)); if (ti != templateInstantiations->end()) templateInstantiations->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; Token::eraseTokens(eqtok, tok2); eqtok->deleteThis(); } } } void TemplateSimplifier::simplifyTemplateAliases(std::list *templateInstantiations) { std::list::iterator it1, it2; for (it1 = templateInstantiations->begin(); it1 != templateInstantiations->end();) { TemplateSimplifier::TokenAndName &templateAlias = *it1; ++it1; Token *startToken = templateAlias.token; while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) startToken = startToken->tokAt(-2); if (!Token::Match(startToken->tokAt(-4), "> using %name% = %name% ::|<")) continue; const std::string aliasName(startToken->strAt(-2)); const Token * const aliasToken1 = startToken; // Get start token for alias startToken = startToken->tokAt(-5); while (Token::Match(startToken, "%name%|<|>|>>|,")) startToken = startToken->previous(); if (!Token::Match(startToken, "[;{}] template <")) continue; // alias parameters.. std::vector aliasParameters; TemplateSimplifier::getTemplateParametersInDeclaration(startToken->tokAt(3), aliasParameters); std::map aliasParameterNames; for (unsigned int argnr = 0; argnr < aliasParameters.size(); ++argnr) aliasParameterNames[aliasParameters[argnr]->str()] = argnr; // Look for alias usages.. const Token *endToken = nullptr; for (it2 = it1; it2 != templateInstantiations->end(); ++it2) { TemplateSimplifier::TokenAndName &aliasUsage = *it2; if (aliasUsage.name != aliasName) 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(); tok2 = tok2->next(); } args.push_back(std::pair(start, tok2)); if (tok2 && tok2->str() == ",") { tok2 = tok2->next(); } else { break; } } if (!tok2 || tok2->str() != ">" || args.size() != aliasParameters.size()) continue; // Replace template alias code.. aliasUsage.name = templateAlias.name; if (aliasUsage.name.find(' ') == std::string::npos) { aliasUsage.token->str(templateAlias.token->str()); } else { tok2 = TokenList::copyTokens(aliasUsage.token, aliasToken1, templateAlias.token, true); aliasUsage.token->deleteThis(); aliasUsage.token = tok2; } tok2 = aliasUsage.token->next(); // the '<' const Token * const endToken1 = templateAlias.token->next()->findClosingBracket(); Token * const endToken2 = TokenList::copyTokens(tok2, templateAlias.token->tokAt(2), endToken1->previous(), false); for (const Token *tok1 = templateAlias.token->next(); tok2 != endToken2; tok1 = tok1->next(), tok2 = tok2->next()) { if (!tok2->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(templateInstantiations->begin(), templateInstantiations->end(), FindToken(tok1)); if (it != templateInstantiations->end()) templateInstantiations->push_back(TokenAndName(tok2, it->scope, it->name)); } continue; } const unsigned int argnr = aliasParameterNames[tok2->str()]; const Token * const fromStart = args[argnr].first; const Token * const fromEnd = args[argnr].second->previous(); Token * const destToken = tok2; tok2 = TokenList::copyTokens(tok2, fromStart, fromEnd, true); if (tok2 == destToken->next()) tok2 = destToken; destToken->deleteThis(); } endToken = endToken1->next(); // Remove alias usage code (parameters) Token::eraseTokens(tok2, args.back().second); } if (endToken) { // Remove all template instantiations in template alias for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { if (!Token::Match(tok, "%name% <")) continue; std::list::iterator it = std::find_if(templateInstantiations->begin(), templateInstantiations->end(), FindToken(tok)); if (it == templateInstantiations->end()) continue; std::list::iterator next = it; ++next; if (it == it1) it1 = next; templateInstantiations->erase(it,next); } Token::eraseTokens(startToken, endToken); } } } bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]) { // if (!Token::simpleMatch(instance, (name + " <").c_str())) // return false; if (numberOfArguments != TemplateSimplifier::templateParameters(instance->next())) return false; if (patternAfter) { const Token *tok = instance; unsigned int indentlevel = 0; for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0); tok = tok->next()) { if (Token::Match(tok, "<|,|(|:: %name% <") && templateParameters(tok->tokAt(2)) > 0) ++indentlevel; if (indentlevel > 0 && tok->str() == ">") --indentlevel; } if (!tok || !Token::Match(tok->next(), patternAfter)) return false; } // nothing mismatching was found.. return true; } // Utility function for TemplateSimplifier::getTemplateNamePosition, that works on template member functions, // hence this pattern: "> %type% [%type%] < ... > :: %type% (" static bool getTemplateNamePositionTemplateMember(const Token *tok, int &namepos) { if (!Token::Match(tok, "> %type% <") && !Token::Match(tok, "> %type% %type% <")) return false; int currPos = 0; currPos = 2 + (Token::Match(tok, "> %type% %type%")); // Find the end of the template argument list const Token *templateParmEnd = tok->linkAt(currPos); if (!templateParmEnd) templateParmEnd = tok->tokAt(currPos)->findClosingBracket(); if (!templateParmEnd) return false; if (Token::Match(templateParmEnd->next(), ":: %type% (")) { // We have a match, and currPos points at the template list opening '<'. Move it to the closing '>' for (const Token *tok2 = tok->tokAt(currPos) ; tok2 != templateParmEnd ; tok2 = tok2->next()) ++currPos; namepos = currPos + 2; return true; } return false; } int TemplateSimplifier::getTemplateNamePosition(const Token *tok) { // get the position of the template name int namepos = 0, starAmpPossiblePosition = 0; if (Token::Match(tok, "> class|struct %type% {|:|<")) namepos = 2; else if (Token::Match(tok, "> %type% *|&| %type% (")) namepos = 2; else if (Token::Match(tok, "> %type% %type% *|&| %type% (")) namepos = 3; else if (getTemplateNamePositionTemplateMember(tok, namepos)) ; else if (Token::Match(tok, "> %type% *|&| %type% :: %type% (")) { namepos = 4; starAmpPossiblePosition = 2; } else if (Token::Match(tok, "> %type% %type% *|&| %type% :: %type% (")) { namepos = 5; starAmpPossiblePosition = 3; } else { // Name not found return -1; } if (Token::Match(tok->tokAt(starAmpPossiblePosition ? starAmpPossiblePosition : namepos), "*|&")) ++namepos; return namepos; } void TemplateSimplifier::expandTemplate( TokenList& tokenlist, const Token *templateDeclarationToken, const std::string &fullName, const std::vector &typeParametersInDeclaration, const std::string &newName, const std::vector &typesUsedInTemplateInstantiation, std::list &templateInstantiations) { std::list scopeInfo; bool inTemplateDefinition = false; const Token *endOfTemplateDefinition = nullptr; const Token * const templateDeclarationNameToken = templateDeclarationToken->tokAt(getTemplateNamePosition(templateDeclarationToken)); for (const Token *tok3 = tokenlist.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { setScopeInfo(const_cast(tok3), &scopeInfo); if (inTemplateDefinition) { if (!endOfTemplateDefinition && tok3->str() == "{") endOfTemplateDefinition = tok3->link(); if (tok3 == endOfTemplateDefinition) inTemplateDefinition = false; } 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 } } if (Token::Match(tok3, "(|[")) tok3 = tok3->link(); // Start of template.. if (tok3 == templateDeclarationToken) { tok3 = tok3->next(); } // member function implemented outside class definition else if (inTemplateDefinition && Token::Match(tok3, "%name% <") && fullName == getFullName(scopeInfo, tok3->str()) && TemplateSimplifier::instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) { // there must be template.. bool istemplate = false; for (const Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { if (prev->str() == "template") { istemplate = true; break; } } if (!istemplate) continue; const Token *tok4 = tok3->next()->findClosingBracket(); while (tok4 && tok4->str() != "(") tok4 = tok4->next(); if (!Tokenizer::isFunctionHead(tok4, ":{", true)) continue; tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); while (tok3 && tok3->str() != "::") tok3 = tok3->next(); } // 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 = (fullName.find(' ') != std::string::npos) ? fullName.substr(fullName.rfind(' ')+1) : fullName; for (; tok3; tok3 = tok3->next()) { if (tok3->isName()) { // 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; for (const Token *typetok = typesUsedInTemplateInstantiation[itype]; typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (Token::simpleMatch(typetok, ". . .")) { typetok = typetok->tokAt(2); continue; } if (Token::Match(typetok, "%name% <") && templateParameters(typetok->next()) > 0) ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; tokenlist.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); tokenlist.back()->isTemplateArg(true); } continue; } } // replace name.. if (tok3 && tok3->str() == lastName) { if (Token::Match(tok3->tokAt(-2), "> :: %name% ( )")) { ; // Ticket #7942: Replacing for out-of-line constructors generates invalid syntax } else if (!Token::simpleMatch(tok3->next(), "<")) { tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); continue; } else if (tok3 == templateDeclarationNameToken) { tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); tok3 = tok3->next()->findClosingBracket(); continue; } } // copy tokenlist.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); if (Token::Match(tok3, "%type% <") && 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 name = tok3->str(); for (const Token *prev = tok3->tokAt(-2); Token::Match(prev, "%name% ::"); prev = prev->tokAt(-2)) name = prev->str() + " :: " + name; templateInstantiations.push_back(TokenAndName(tokenlist.back(), getScopeName(scopeInfo), getFullName(scopeInfo, name))); } // link() newly tokens manually else if (tok3->str() == "{") { brackets.push(tokenlist.back()); } else if (tok3->str() == "(") { brackets.push(tokenlist.back()); } else if (tok3->str() == "[") { brackets.push(tokenlist.back()); } else if (tok3->str() == "}") { assert(brackets.empty() == false && brackets.top()->str() == "{"); Token::createMutualLinks(brackets.top(), tokenlist.back()); if (tok3->strAt(1) == ";") { const Token * tokSemicolon = tok3->next(); tokenlist.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex()); } brackets.pop(); if (brackets.empty()) { inTemplateDefinition = false; break; } } else if (tok3->str() == ")") { assert(brackets.empty() == false && brackets.top()->str() == "("); Token::createMutualLinks(brackets.top(), tokenlist.back()); brackets.pop(); } else if (tok3->str() == "]") { assert(brackets.empty() == false && brackets.top()->str() == "["); Token::createMutualLinks(brackets.top(), tokenlist.back()); brackets.pop(); } } assert(brackets.empty()); } } 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 ret = false; // (1-2) while (tok->tokAt(4) && tok->next()->isNumber() && tok->tokAt(3)->isNumber()) { // %any% %num% %any% %num% %any% const Token* op = tok->tokAt(2); const Token* after = tok->tokAt(4); if (Token::Match(tok, "* %num% /") && (op->strAt(1) != "0") && tok->next()->str() == MathLib::multiply(op->strAt(1), MathLib::divide(tok->next()->str(), op->strAt(1)))) { // Division where result is a whole number } else if (!((op->str() == "*" && (isLowerThanMulDiv(tok) || tok->str() == "*") && isLowerEqualThanMulDiv(after)) || // associative (Token::Match(op, "[/%]") && isLowerThanMulDiv(tok) && isLowerEqualThanMulDiv(after)) || // NOT associative (Token::Match(op, "[+-]") && isLowerThanMulDiv(tok) && isLowerThanMulDiv(after)) || // Only partially (+) associative, but handled later (Token::Match(op, ">>|<<") && isLowerThanShift(tok) && isLowerThanPlusMinus(after)) || // NOT associative (op->str() == "&" && isLowerThanShift(tok) && isLowerThanShift(after)) || // associative (op->str() == "^" && isLowerThanAnd(tok) && isLowerThanAnd(after)) || // associative (op->str() == "|" && isLowerThanXor(tok) && isLowerThanXor(after)) || // associative (op->str() == "&&" && isLowerThanOr(tok) && isLowerThanOr(after)) || (op->str() == "||" && isLowerThanLogicalAnd(tok) && isLowerThanLogicalAnd(after)))) break; tok = tok->next(); // Don't simplify "%num% / 0" if (Token::Match(op, "[/%] 0")) continue; // 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(tok->str()) || MathLib::isNegative(tok->strAt(2))) continue; const MathLib::value v1(tok->str()); const MathLib::value v2(tok->strAt(2)); if (!v1.isInt() || !v2.isInt()) continue; 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%|&&")) { bool op1 = !MathLib::isNullValue(tok->str()); bool op2 = !MathLib::isNullValue(tok->strAt(2)); 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(tok->str(), tok->strAt(2))); else if (Token::Match(tok->previous(), "- %num% + %num%")) tok->str(MathLib::subtract(tok->str(), tok->strAt(2))); else { try { tok->str(MathLib::calculate(tok->str(), tok->strAt(2), op->str()[0])); } catch (InternalError &e) { e.token = tok; throw; } } tok->deleteNext(2); tok = tok->previous(); ret = true; } if (Token::Match(tok, "%oror%|&& %num% %oror%|&&|,|)") || Token::Match(tok, "[(,] %num% %oror%|&&")) { tok->next()->str(MathLib::isNullValue(tok->next()->str()) ? "0" : "1"); } return ret; } // TODO: This is not the correct class for simplifyCalculations(), so it // should be moved away. bool TemplateSimplifier::simplifyCalculations(Token *_tokens) { bool ret = false, goback = false; for (Token *tok = _tokens; tok; tok = tok->next()) { if (goback) { tok = tok->previous(); goback = false; } // 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 ((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 (Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { tok->str(MathLib::toString(MathLib::toLongNumber(tok->str()))); } if (tok->isNumber()) { // Remove redundant conditions (0&&x) (1||x) if (Token::Match(tok->previous(), "[(=,] 0 &&") || Token::Match(tok->previous(), "[(=,] 1 %oror%")) { unsigned int par = 0; const Token *tok2 = tok; 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) { Token::eraseTokens(tok, tok2); ret = true; } continue; } if (tok->str() == "0") { if ((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 (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(), "[=[(,] 0 * (") || Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 *|&& (")) { tok->deleteNext(); if (tok->next()->str() == "(") Token::eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } else if (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%")) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") Token::eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } } if (tok->str() == "1") { if (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%")) { tok->deleteNext(); if (tok->next()->str() == "(") Token::eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } else if (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() == "(") Token::eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } } if (Token::Match(tok->tokAt(-2), "%any% * 1") || Token::Match(tok->previous(), "%any% 1 *")) { tok = tok->previous(); if (tok->str() == "*") tok = tok->previous(); tok->deleteNext(2); ret = true; } // Remove parentheses around number.. if (Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && tok->strAt(-2) != ">") { tok = tok->previous(); tok->deleteThis(); tok->deleteNext(); ret = true; } if (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 (Token::Match(tok, "%num% %comp% %num%") && MathLib::isInt(tok->str()) && MathLib::isInt(tok->strAt(2))) { if (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; goback = true; } } } // Division where result is a whole number else if (Token::Match(tok->previous(), "* %num% /") && tok->str() == MathLib::multiply(tok->strAt(2), MathLib::divide(tok->str(), tok->strAt(2)))) { tok->deleteNext(2); } else if (simplifyNumericCalculations(tok)) { ret = true; while (Token::Match(tok->tokAt(-2), "%cop%|,|( %num% %cop% %num% %cop%|,|)")) { Token *before = tok->tokAt(-2); if (simplifyNumericCalculations(before)) tok = before; else break; } } } return ret; } const Token * TemplateSimplifier::getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration) { typeParametersInDeclaration.clear(); for (; tok && tok->str() != ">"; tok = tok->next()) { if (Token::Match(tok, "%name% ,|>")) typeParametersInDeclaration.push_back(tok); } return tok; } static bool 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; TemplateSimplifier::getTemplateParametersInDeclaration(startToken->tokAt(2), templateParameters); const Token *instToken = templateInstantiationNameToken->tokAt(2); const Token *declToken = (*it)->tokAt(2); const Token * const endToken = (*it)->next()->findClosingBracket(); 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 == 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% !!<"); } bool TemplateSimplifier::simplifyTemplateInstantiations( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, const TokenAndName &templateDeclaration, const std::list &specializations, const std::time_t maxtime, std::list &templateInstantiations, 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; const Token * const tok = getTemplateParametersInDeclaration(templateDeclaration.token->tokAt(2), typeParametersInDeclaration); // bail out if the end of the file was reached if (!tok) return false; const bool printDebug = _settings->debugwarnings; // get the position of the template name const int namepos = TemplateSimplifier::getTemplateNamePosition(tok); if (namepos == -1) { // debug message that we bail out.. if (printDebug && errorlogger) { std::list callstack(1, tok); errorlogger->reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug", "simplifyTemplates: bailing out", false)); } return false; } // name of template function/class.. const std::string name(tok->strAt(namepos)); const bool isfunc(tok->strAt(namepos + 1) == "("); // locate template usage.. std::string::size_type numberOfTemplateInstantiations = templateInstantiations.size(); unsigned int recursiveCount = 0; bool instantiated = false; for (std::list::const_iterator iter2 = templateInstantiations.begin(); iter2 != templateInstantiations.end(); ++iter2) { if (numberOfTemplateInstantiations != templateInstantiations.size()) { numberOfTemplateInstantiations = templateInstantiations.size(); simplifyCalculations(tokenlist.front()); ++recursiveCount; if (recursiveCount > 100) { // bail out.. break; } } // already simplified if (!Token::Match(iter2->token, "%name% <")) continue; if (iter2->name != templateDeclaration.name) continue; if (!matchSpecialization(tok->tokAt(namepos), iter2->token, specializations)) continue; Token * const tok2 = iter2->token; if (errorlogger && !tokenlist.getFiles().empty()) errorlogger->reportProgress(tokenlist.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); #ifdef MAXTIME if (std::time(0) > maxtime) return false; #else (void)maxtime; #endif assert(tokenlist.validateToken(tok2)); // that assertion fails on examples from #6021 const Token *startToken = tok2; while (Token::Match(startToken->tokAt(-2), "%name% :: %name%")) startToken = startToken->tokAt(-2); if (Token::Match(startToken->previous(), "[;{}=]") && !TemplateSimplifier::instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %name%")) continue; // New type.. std::vector typesUsedInTemplateInstantiation; std::string typeForNewName; std::list typeStringsUsedInTemplateInstantiation; unsigned int indentlevel = 0; for (const Token *tok3 = tok2->tokAt(2); tok3 && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { // #2648 - unhandled parentheses => bail out // #2721 - unhandled [ => bail out if (Token::Match(tok3, "(|[")) { typeForNewName.clear(); break; } if (!tok3->next()) { typeForNewName.clear(); break; } if (Token::Match(tok3->tokAt(-2), "[<,] %name% <") && templateParameters(tok3) > 0) ++indentlevel; else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) --indentlevel; if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) typesUsedInTemplateInstantiation.push_back(tok3); 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(); } } if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) { if (printDebug && errorlogger) { std::list callstack(1, tok2); errorlogger->reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug", "Failed to instantiate template \"" + iter2->name + "\". The checking continues anyway.", false)); } if (typeForNewName.empty()) continue; break; } // New classname/funcname.. const std::string newName(templateDeclaration.name + " < " + typeForNewName + " >"); if (expandedtemplates.find(newName) == expandedtemplates.end()) { expandedtemplates.insert(newName); TemplateSimplifier::expandTemplate(tokenlist, tok, iter2->name, typeParametersInDeclaration, newName, typesUsedInTemplateInstantiation, templateInstantiations); instantiated = true; } // Replace all these template usages.. replaceTemplateUsage(tok2, iter2->name, typeStringsUsedInTemplateInstantiation, newName, typesUsedInTemplateInstantiation, templateInstantiations); } // 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(Token * const instantiationToken, const std::string &templateName, const std::list &typeStringsUsedInTemplateInstantiation, const std::string &newName, const std::vector &typesUsedInTemplateInstantiation, std::list &templateInstantiations) { std::list scopeInfo; std::list< std::pair > removeTokens; for (Token *nameTok = instantiationToken; nameTok; nameTok = nameTok->next()) { setScopeInfo(nameTok, &scopeInfo); if (!Token::Match(nameTok, "%name% <")) continue; if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation)) continue; // FIXME Proper name matching const std::string lastName(templateName.find(' ') == std::string::npos ? templateName : templateName.substr(templateName.rfind(' ') + 1)); if (lastName != nameTok->str()) continue; // match parameters Token * tok2 = nameTok->tokAt(2); unsigned int typeCountInInstantiation = 1U; // There is always at least one type const Token *typetok = (!typesUsedInTemplateInstantiation.empty()) ? typesUsedInTemplateInstantiation[0] : nullptr; unsigned int indentlevel2 = 0; // indentlevel for tokgt while (tok2 && (indentlevel2 > 0 || tok2->str() != ">")) { if (tok2->str() == "<" && templateParameters(tok2) > 0) ++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 < typesUsedInTemplateInstantiation.size()) typetok = typesUsedInTemplateInstantiation[typeCountInInstantiation++]; else typetok = nullptr; } } tok2 = tok2->next(); } if (!tok2) break; // matching template usage => replace tokens.. // Foo < int > => Foo if (tok2->str() == ">" && typeCountInInstantiation == typesUsedInTemplateInstantiation.size()) { Token * const nameTok1 = nameTok; while (Token::Match(nameTok->tokAt(-2), "%name% :: %name%")) nameTok = nameTok->tokAt(-2); nameTok->str(newName); for (std::list::iterator it = templateInstantiations.begin(); it != templateInstantiations.end(); ++it) { if (it->token == nameTok1) it->token = nameTok; } for (Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) { if (tok->isName()) { std::list::iterator ti; for (ti = templateInstantiations.begin(); ti != templateInstantiations.end();) { if (ti->token == tok) templateInstantiations.erase(ti++); else ++ti; } } } removeTokens.push_back(std::pair(nameTok, tok2->next())); } nameTok = tok2; } while (!removeTokens.empty()) { Token::eraseTokens(removeTokens.back().first, removeTokens.back().second); removeTokens.pop_back(); } } void TemplateSimplifier::simplifyTemplates( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, const std::time_t maxtime, bool &_codeWithTemplates ) { std::set expandedtemplates(TemplateSimplifier::expandSpecialized(tokenlist.front())); if (TemplateSimplifier::getTemplateDeclarations(tokenlist.front(), _codeWithTemplates).empty()) return; // There are templates.. // Remove "typename" unless used in template arguments.. for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { if (tok->str() == "typename") tok->deleteThis(); if (Token::simpleMatch(tok, "template <")) { while (tok && tok->str() != ">") tok = tok->next(); if (!tok) break; } } std::list templateDeclarations(TemplateSimplifier::getTemplateDeclarations(tokenlist.front(), _codeWithTemplates)); // Locate possible instantiations of templates.. std::list templateInstantiations(TemplateSimplifier::getTemplateInstantiations(tokenlist.front(), templateDeclarations)); // No template instantiations? Then return. if (templateInstantiations.empty()) return; // Template arguments with default values TemplateSimplifier::useDefaultArgumentValues(templateDeclarations, &templateInstantiations); TemplateSimplifier::simplifyTemplateAliases(&templateInstantiations); // expand templates //bool done = false; //while (!done) { //done = true; std::list instantiatedTemplates; for (std::list::reverse_iterator iter1 = templateDeclarations.rbegin(); iter1 != templateDeclarations.rend(); ++iter1) { // get specializations.. std::list specializations; for (std::list::const_iterator iter2 = templateDeclarations.begin(); iter2 != templateDeclarations.end(); ++iter2) { if (iter1->name == iter2->name) { const Token *tok = iter2->token->next()->findClosingBracket(); int namepos = getTemplateNamePosition(tok); if (namepos > 0) specializations.push_back(tok->tokAt(namepos)); } } bool instantiated = TemplateSimplifier::simplifyTemplateInstantiations(tokenlist, errorlogger, _settings, *iter1, specializations, maxtime, templateInstantiations, expandedtemplates); if (instantiated) instantiatedTemplates.push_back(*iter1); } for (std::list::const_iterator it = instantiatedTemplates.begin(); it != instantiatedTemplates.end(); ++it) { std::list::iterator it1; for (it1 = templateDeclarations.begin(); it1 != templateDeclarations.end(); ++it1) { if (it1->token == it->token) break; } if (it1 != templateDeclarations.end()) { templateDeclarations.erase(it1); removeTemplate(it->token); } } } } void TemplateSimplifier::syntaxError(const Token *tok) { throw InternalError(tok, "syntax error", InternalError::SYNTAX); } cppcheck-1.82/lib/templatesimplifier.h000066400000000000000000000225641322667425100200630ustar00rootroot00000000000000/* * 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 templatesimplifierH #define templatesimplifierH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include class ErrorLogger; class Settings; class Token; class TokenList; /// @addtogroup Core /// @{ /** @brief Simplify templates from the preprocessed and partially simplified code. */ class CPPCHECKLIB TemplateSimplifier { TemplateSimplifier(); ~TemplateSimplifier(); public: /** * 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. */ static void cleanupAfterSimplify(Token *tokens); /** * \param[in] tokens token list * @return false if there are no syntax errors or true */ static void checkComplicatedSyntaxErrorsInTemplates(const Token *tokens); /** * 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); /** * Expand specialized templates : "template<>.." * @return names of expanded templates */ static std::set expandSpecialized(Token *tokens); /** * Token and its full scopename */ struct TokenAndName { TokenAndName(Token *tok, const std::string &s, const std::string &n) : token(tok), scope(s), name(n) {} Token *token; std::string scope; std::string name; }; /** * Get template declarations * @return list of template declarations */ static std::list getTemplateDeclarations(Token *tokens, bool &codeWithTemplates); /** * Get template instantiations * @param tokens start of token list * @param declarations template declarations, so names can be matched * @return list of template instantiations */ static std::list getTemplateInstantiations(Token *tokens, const std::list &declarations); /** * simplify template instantiations (use default argument values) * @param templates list of template declarations * @param templateInstantiations list of template instantiations */ static void useDefaultArgumentValues(const std::list &templates, std::list *templateInstantiations); /** * simplify template aliases * @param templateInstantiations pointer to list of template instantiations */ static void simplifyTemplateAliases(std::list *templateInstantiations); /** * 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. */ static int getTemplateNamePosition(const Token *tok); /** * Expand a template. Create "expanded" class/function at end of tokenlist. * @param tokenlist The tokenlist that is changed * @param templateDeclarationToken The template declaration token for the template that will be "expanded" * @param fullName Full name of template * @param typeParametersInDeclaration The type parameters of the template * @param newName New name of class/function. * @param typesUsedInTemplateInstantiation Type parameters in instantiation * @param templateInstantiations List of template instantiations. */ static void expandTemplate( TokenList& tokenlist, const Token *templateDeclarationToken, const std::string &fullName, const std::vector &typeParametersInDeclaration, const std::string &newName, const std::vector &typesUsedInTemplateInstantiation, std::list &templateInstantiations); /** * @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); /** * Simplify templates : expand all instantiations for a template * @todo It seems that inner templates should be instantiated recursively * @param tokenlist token list * @param errorlogger error logger * @param _settings settings * @param templateDeclaration template declaration * @param specializations template specializations (list each template name token) * @param maxtime time when the simplification will stop * @param templateInstantiations a list of template usages (not necessarily just for this template) * @param expandedtemplates all templates that has been expanded so far. The full names are stored. * @return true if the template was instantiated */ static bool simplifyTemplateInstantiations( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, const TokenAndName &templateDeclaration, const std::list &specializations, const std::time_t maxtime, std::list &templateInstantiations, std::set &expandedtemplates); /** * Replace all matching template usages 'Foo < int >' => 'Foo' * @param instantiationToken Template instantiation token * @param templateName full template name with scope info * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param newName The new type name * @param typesUsedInTemplateInstantiation template instantiation parameters * @param templateInstantiations All seen instantiations */ static void replaceTemplateUsage(Token *const instantiationToken, const std::string &templateName, const std::list &typeStringsUsedInTemplateInstantiation, const std::string &newName, const std::vector &typesUsedInTemplateInstantiation, std::list &templateInstantiations); /** * Simplify templates * @param tokenlist token list * @param errorlogger error logger * @param _settings settings * @param maxtime time when the simplification should be stopped * @param _codeWithTemplates output parameter that is set if code contains templates */ static void simplifyTemplates( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, 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); /** * Simplify constant calculations such as "1+2" => "3". * This also performs simple cleanup of parentheses etc. * @param _tokens start token * @return true if modifications to token-list are done. * false if no modifications are done. */ static bool simplifyCalculations(Token *_tokens); private: /** * Remove a specific "template < ..." template class/function */ static bool removeTemplate(Token *tok); /** Syntax error */ static void syntaxError(const Token *tok); }; /// @} //--------------------------------------------------------------------------- #endif // templatesimplifierH cppcheck-1.82/lib/timer.cpp000066400000000000000000000063671322667425100156420ustar00rootroot00000000000000/* * 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 "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_NONE) return; std::cout << std::endl; TimerResultsData overallData; std::vector data(_results.begin(), _results.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._numberOfResults); overallData._clocks += iter->second._clocks; if ((mode != SHOWTIME_TOP5) || (ordinal<=5)) { std::cout << iter->first << ": " << sec << "s (avg. " << secAverage << "s - " << iter->second._numberOfResults << " 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) { _results[str]._clocks += clocks; _results[str]._numberOfResults++; } Timer::Timer(const std::string& str, unsigned int showtimeMode, TimerResultsIntf* timerResults) : _str(str) , _timerResults(timerResults) , _start(0) , _showtimeMode(showtimeMode) , _stopped(false) { if (showtimeMode != SHOWTIME_NONE) _start = std::clock(); } Timer::~Timer() { Stop(); } void Timer::Stop() { if ((_showtimeMode != SHOWTIME_NONE) && !_stopped) { const std::clock_t end = std::clock(); const std::clock_t diff = end - _start; if (_showtimeMode == SHOWTIME_FILE) { double sec = (double)diff / CLOCKS_PER_SEC; std::cout << _str << ": " << sec << "s" << std::endl; } else { if (_timerResults) _timerResults->AddResults(_str, diff); } } _stopped = true; } cppcheck-1.82/lib/timer.h000066400000000000000000000045541322667425100153030ustar00rootroot00000000000000/* * 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 timerH #define timerH //--------------------------------------------------------------------------- #include "config.h" #include #include #include enum 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 _clocks; long _numberOfResults; TimerResultsData() : _clocks(0) , _numberOfResults(0) { } double seconds() const { double ret = (double)((unsigned long)_clocks) / (double)CLOCKS_PER_SEC; return ret; } }; class CPPCHECKLIB TimerResults : public TimerResultsIntf { public: TimerResults() { } void ShowResults(SHOWTIME_MODES mode) const; virtual void AddResults(const std::string& str, std::clock_t clocks); private: std::map _results; }; class CPPCHECKLIB Timer { public: Timer(const std::string& str, unsigned int showtimeMode, TimerResultsIntf* timerResults = nullptr); ~Timer(); void Stop(); private: Timer(const Timer& other); // disallow copying Timer& operator=(const Timer&); // disallow assignments const std::string _str; TimerResultsIntf* _timerResults; std::clock_t _start; const unsigned int _showtimeMode; bool _stopped; }; //--------------------------------------------------------------------------- #endif // timerH cppcheck-1.82/lib/token.cpp000066400000000000000000001367241322667425100156430ustar00rootroot00000000000000/* * 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 "token.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 Token::emptyValueList; Token::Token(Token **tokens) : tokensBack(tokens), _next(nullptr), _previous(nullptr), _link(nullptr), _scope(nullptr), _function(nullptr), // Initialize whole union _varId(0), _fileIndex(0), _linenr(0), _col(0), _progressValue(0), _tokType(eNone), _flags(0), _astOperand1(nullptr), _astOperand2(nullptr), _astParent(nullptr), _originalName(nullptr), valuetype(nullptr), _values(nullptr) { } Token::~Token() { delete _originalName; delete valuetype; delete _values; } static const std::set controlFlowKeywords = make_container< std::set > () << "goto" << "do" << "if" << "else" << "for" << "while" << "switch" << "case" << "break" << "continue" << "return"; void Token::update_property_info() { setFlag(fIsControlFlowKeyword, controlFlowKeywords.find(_str) != controlFlowKeywords.end()); if (!_str.empty()) { if (_str == "true" || _str == "false") tokType(eBoolean); else if (std::isalpha((unsigned char)_str[0]) || _str[0] == '_' || _str[0] == '$') { // Name if (_varId) tokType(eVariable); else if (_tokType != eVariable && _tokType != eFunction && _tokType != eType && _tokType != eKeyword) tokType(eName); } else if (std::isdigit((unsigned char)_str[0]) || (_str.length() > 1 && _str[0] == '-' && std::isdigit((unsigned char)_str[1]))) tokType(eNumber); else if (_str.length() > 1 && _str[0] == '"' && endsWith(_str,'"')) tokType(eString); else if (_str.length() > 1 && _str[0] == '\'' && endsWith(_str,'\'')) tokType(eChar); else if (_str == "=" || _str == "<<=" || _str == ">>=" || (_str.size() == 2U && _str[1] == '=' && std::strchr("+-*/%&^|", _str[0]))) tokType(eAssignmentOp); else if (_str.size() == 1 && _str.find_first_of(",[]()?:") != std::string::npos) tokType(eExtendedOp); else if (_str=="<<" || _str==">>" || (_str.size()==1 && _str.find_first_of("+-*/%") != std::string::npos)) tokType(eArithmeticalOp); else if (_str.size() == 1 && _str.find_first_of("&|^~") != std::string::npos) tokType(eBitOp); else if (_str.size() <= 2 && (_str == "&&" || _str == "||" || _str == "!")) tokType(eLogicalOp); else if (_str.size() <= 2 && !_link && (_str == "==" || _str == "!=" || _str == "<" || _str == "<=" || _str == ">" || _str == ">=")) tokType(eComparisonOp); else if (_str.size() == 2 && (_str == "++" || _str == "--")) tokType(eIncDecOp); else if (_str.size() == 1 && (_str.find_first_of("{}") != std::string::npos || (_link && _str.find_first_of("<>") != std::string::npos))) tokType(eBracket); else tokType(eOther); } else { tokType(eNone); } update_property_isStandardType(); } static const std::set stdTypes = make_container >() << "bool" << "_Bool" << "char" << "double" << "float" << "int" << "long" << "short" << "size_t" << "void" << "wchar_t"; void Token::update_property_isStandardType() { isStandardType(false); if (_str.size() < 3) return; if (stdTypes.find(_str)!=stdTypes.end()) { isStandardType(true); tokType(eType); } } bool Token::isUpperCaseName() const { if (!isName()) return false; for (size_t i = 0; i < _str.length(); ++i) { if (std::islower(_str[i])) return false; } return true; } void Token::concatStr(std::string const& b) { _str.erase(_str.length() - 1); _str.append(b.begin() + 1, b.end()); update_property_info(); } std::string Token::strValue() const { assert(_tokType == eString); std::string ret(_str.substr(1, _str.length() - 2)); 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(unsigned long index) { while (_next && index) { Token *n = _next; // #8154 we are about to be unknown -> destroy the link to us if (n->_link && n->_link->_link == n) n->_link->link(nullptr); _next = n->next(); delete n; --index; } if (_next) _next->previous(this); else if (tokensBack) *tokensBack = this; } void Token::swapWithNext() { if (_next) { std::swap(_str, _next->_str); std::swap(_tokType, _next->_tokType); std::swap(_flags, _next->_flags); std::swap(_varId, _next->_varId); std::swap(_fileIndex, _next->_fileIndex); std::swap(_linenr, _next->_linenr); if (_next->_link) _next->_link->_link = this; if (this->_link) this->_link->_link = _next; std::swap(_link, _next->_link); std::swap(_scope, _next->_scope); std::swap(_function, _next->_function); std::swap(_originalName, _next->_originalName); std::swap(_values, _next->_values); std::swap(valuetype, _next->valuetype); std::swap(_progressValue, _next->_progressValue); } } void Token::takeData(Token *fromToken) { _str = fromToken->_str; tokType(fromToken->_tokType); _flags = fromToken->_flags; _varId = fromToken->_varId; _fileIndex = fromToken->_fileIndex; _linenr = fromToken->_linenr; _link = fromToken->_link; _scope = fromToken->_scope; _function = fromToken->_function; if (fromToken->_originalName) { delete _originalName; _originalName = fromToken->_originalName; fromToken->_originalName = nullptr; } delete _values; _values = fromToken->_values; fromToken->_values = nullptr; delete valuetype; valuetype = fromToken->valuetype; fromToken->valuetype = nullptr; if (_link) _link->link(this); } void Token::deleteThis() { if (_next) { // Copy next to this and delete next takeData(_next); _next->link(nullptr); // mark as unlinked deleteNext(); } else if (_previous && _previous->_previous) { // Copy previous to this and delete previous takeData(_previous); Token* toDelete = _previous; _previous = _previous->_previous; _previous->_next = 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->tokensBack && *(end->tokensBack) == end) { while (end->next()) end = end->next(); *(end->tokensBack) = end; } // Update _progressValue, fileIndex and linenr for (Token *tok = start; tok != end->next(); tok = tok->next()) tok->_progressValue = replaceThis->_progressValue; // 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->_str : emptyString; } static int multiComparePercent(const Token *tok, const char*& haystack, unsigned 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, unsigned 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] != ' ') { 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) { std::size_t length = next - current; if (!tok || length != tok->_str.length() || std::strncmp(current, tok->_str.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[], unsigned 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; unsigned 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 { 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; } std::size_t Token::getStrLength(const Token *tok) { assert(tok != nullptr); assert(tok->_tokType == eString); std::size_t len = 0; std::string::const_iterator it = tok->str().begin() + 1U; const std::string::const_iterator end = tok->str().end() - 1U; while (it != end) { if (*it == '\\') { ++it; // string ends at '\0' if (*it == '0') return len; } if (*it == '\0') return len; ++it; ++len; } return len; } std::size_t Token::getStrSize(const Token *tok) { assert(tok != nullptr && tok->tokType() == eString); const std::string &str = tok->str(); unsigned int sizeofstring = 1U; for (unsigned int i = 1U; i < str.size() - 1U; i++) { if (str[i] == '\\') ++i; ++sizeofstring; } return sizeofstring; } std::string Token::getCharAt(const Token *tok, std::size_t index) { assert(tok != nullptr); std::string::const_iterator it = tok->str().begin() + 1U; const std::string::const_iterator end = tok->str().end() - 1U; 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->_progressValue = newLocation->_progressValue; } 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; } const Token * Token::findClosingBracket() const { if (_str != "<") return nullptr; const Token *closing = nullptr; 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; else if (closing->str() == "<") ++depth; else if (closing->str() == ">") { if (--depth == 0) return closing; } else if (closing->str() == ">>") { if (depth <= 2) return closing; depth -= 2; } } return closing; } Token * Token::findClosingBracket() { // return value of const function return const_cast(const_cast(this)->findClosingBracket()); } //--------------------------------------------------------------------------- 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 unsigned 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 unsigned 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::insertToken(const std::string &tokenStr, const std::string &originalNameStr, bool prepend) { //TODO: Find a solution for the first token on the list if (prepend && !this->previous()) return; Token *newToken; if (_str.empty()) newToken = this; else newToken = new Token(tokensBack); newToken->str(tokenStr); if (!originalNameStr.empty()) newToken->originalName(originalNameStr); newToken->_linenr = _linenr; newToken->_fileIndex = _fileIndex; newToken->_progressValue = _progressValue; if (newToken != this) { if (prepend) { /*if (this->previous())*/ { newToken->previous(this->previous()); newToken->previous()->next(newToken); } /*else if (tokensFront?) { *tokensFront? = newToken; }*/ this->previous(newToken); newToken->next(this); } else { if (this->next()) { newToken->next(this->next()); newToken->next()->previous(newToken); } else if (tokensBack) { *tokensBack = newToken; } this->next(newToken); newToken->previous(this); } } } 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 (_tokType == eString || _tokType == eChar) os << "L"; else os << "long "; } } if (macro && isExpandedMacro()) os << "$"; if (isName() && _str.find(' ') != std::string::npos) { for (std::size_t i = 0U; i < _str.size(); ++i) { if (_str[i] != ' ') os << _str[i]; } } else if (_str[0] != '\"' || _str.find('\0') == std::string::npos) os << _str; else { for (std::size_t i = 0U; i < _str.size(); ++i) { if (_str[i] == '\0') os << "\\0"; else os << _str[i]; } } if (varid && _varId != 0) os << '@' << _varId; } 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 = _linenr - (linenumbers ? 1U : 0U); unsigned int fileInd = files ? ~0U : _fileIndex; std::map lineNumbers; for (const Token *tok = this; tok != end; tok = tok->next()) { bool fileChange = false; if (tok->_fileIndex != fileInd) { if (fileInd != ~0U) { lineNumbers[fileInd] = tok->_fileIndex; } fileInd = tok->_fileIndex; if (files) { ret << "\n\n##file "; if (fileNames && fileNames->size() > tok->_fileIndex) ret << fileNames->at(tok->_fileIndex); else ret << fileInd; ret << '\n'; } lineNumber = lineNumbers[fileInd]; fileChange = true; } if (linebreaks && (lineNumber != tok->linenr() || fileChange)) { if (lineNumber+4 < tok->linenr() && fileInd == tok->_fileIndex) { 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) { const Token* const root = tok; if (_astOperand1) _astOperand1->_astParent = nullptr; // goto parent operator if (tok) { while (tok->_astParent) { if (tok->_astParent == this || tok->_astParent == root) // #6838/#6726 avoid hang on garbage code throw InternalError(this, "Internal error. Token::astOperand1() cyclic dependency."); tok = tok->_astParent; } tok->_astParent = this; } _astOperand1 = tok; } void Token::astOperand2(Token *tok) { const Token* const root = tok; if (_astOperand2) _astOperand2->_astParent = nullptr; // goto parent operator if (tok) { while (tok->_astParent) { //std::cout << tok << " -> " << tok->_astParent ; if (tok->_astParent == this || tok->_astParent == root) // #6838/#6726 avoid hang on garbage code throw InternalError(this, "Internal error. Token::astOperand2() cyclic dependency."); tok = tok->_astParent; } tok->_astParent = this; } _astOperand2 = tok; } 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 = _previous; const Token *tokafter = _next; for (int distance = 1; distance < 10 && tokbefore; distance++) { if (tokbefore == _astOperand1) return false; if (tokafter == _astOperand1) return true; tokbefore = tokbefore->_previous; tokafter = tokafter->_previous; } return false; // <- guess } 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; } 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()) ret << (tok->isLiteral() ? "L" : "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 Token * const top = this; const Token *start = top; while (start->astOperand1() && (start->astOperand2() || !start->isUnaryPreOp() || Token::simpleMatch(start, "( )"))) start = start->astOperand1(); const Token *end = top; while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { if (Token::Match(end,"(|[") && !(Token::Match(end, "( %type%") && !end->astOperand2())) { end = end->link(); break; } end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); } start = goToLeftParenthesis(start, end); end = goToRightParenthesis(start, end); return stringFromTokenRange(start, end); } static void astStringXml(const Token *tok, std::size_t 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->_astParent && tok->_astOperand1) { 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() << "\">" << std::endl; astStringXml(tok, 2U, out); out << "" << std::endl; } else if (verbose) out << tok->astStringVerbose(0,0) << std::endl; else out << tok->astString(" ") << std::endl; if (tok->str() == "(") tok = tok->link(); } } } static std::string indent(const unsigned int indent1, const unsigned int indent2) { std::string ret(indent1,' '); for (unsigned int i = indent1; i < indent2; i += 2) ret += "| "; return ret; } std::string Token::astStringVerbose(const unsigned int indent1, const unsigned int indent2) const { std::string ret; if (isExpandedMacro()) ret += '$'; ret += _str; if (valuetype) ret += " \'" + valuetype->str() + '\''; ret += '\n'; if (_astOperand1) { unsigned int i1 = indent1, i2 = indent2 + 2; if (indent1==indent2 && !_astOperand2) i1 += 2; ret += indent(indent1,indent2) + (_astOperand2 ? "|-" : "`-") + _astOperand1->astStringVerbose(i1,i2); } if (_astOperand2) { unsigned int i1 = indent1, i2 = indent2 + 2; if (indent1==indent2) i1 += 2; ret += indent(indent1,indent2) + "`-" + _astOperand2->astStringVerbose(i1,i2); } return ret; } void Token::printValueFlow(bool xml, std::ostream &out) const { unsigned 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->_values) continue; if (xml) out << " _values << "\">" << std::endl; else if (line != tok->linenr()) out << "Line " << tok->linenr() << std::endl; line = tok->linenr(); if (!xml) { out << " " << tok->str() << (tok->_values->front().isKnown() ? " always " : " possible "); if (tok->_values->size() > 1U) out << '{'; } for (std::list::const_iterator it=tok->_values->begin(); it!=tok->_values->end(); ++it) { if (xml) { out << " valueType) { case ValueFlow::Value::INT: if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED) out << "intvalue=\"" << (MathLib::biguint)it->intvalue << '\"'; else out << "intvalue=\"" << it->intvalue << '\"'; break; case ValueFlow::Value::TOK: out << "tokvalue=\"" << it->tokvalue << '\"'; break; case ValueFlow::Value::FLOAT: out << "floatvalue=\"" << it->floatValue << '\"'; break; case ValueFlow::Value::MOVED: out << "movedvalue=\"" << ValueFlow::Value::toString(it->moveKind) << '\"'; break; case ValueFlow::Value::UNINIT: out << "uninit=\"1\""; break; } if (it->condition) out << " condition-line=\"" << it->condition->linenr() << '\"'; if (it->isKnown()) out << " known=\"true\""; else if (it->isPossible()) out << " possible=\"true\""; else if (it->isInconclusive()) out << " inconclusive=\"true\""; out << "/>" << std::endl; } else { if (it != tok->_values->begin()) out << ","; switch (it->valueType) { case ValueFlow::Value::INT: if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED) out << (MathLib::biguint)it->intvalue; else out << it->intvalue; break; case ValueFlow::Value::TOK: out << it->tokvalue->str(); break; case ValueFlow::Value::FLOAT: out << it->floatValue; break; case ValueFlow::Value::MOVED: out << ValueFlow::Value::toString(it->moveKind); break; case ValueFlow::Value::UNINIT: out << "Uninit"; break; } } } if (xml) out << " " << std::endl; else if (tok->_values->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 (!_values) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = _values->begin(); it != _values->end(); ++it) { 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 (!_values) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = _values->begin(); it != _values->end(); ++it) { 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, unsigned int argnr, const Settings *settings) const { if (!_values) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = _values->begin(); it != _values->end(); ++it) { if (it->isIntValue() && !settings->library.isargvalid(ftok, argnr, it->intvalue)) { 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 Token *Token::getValueTokenMinStrSize() const { if (!_values) return nullptr; const Token *ret = nullptr; std::size_t minsize = ~0U; std::list::const_iterator it; for (it = _values->begin(); it != _values->end(); ++it) { if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { std::size_t size = getStrSize(it->tokvalue); if (!ret || size < minsize) { minsize = size; ret = it->tokvalue; } } } return ret; } const Token *Token::getValueTokenMaxStrLength() const { if (!_values) return nullptr; const Token *ret = nullptr; std::size_t maxlength = 0U; std::list::const_iterator it; for (it = _values->begin(); it != _values->end(); ++it) { if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { std::size_t 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; } bool Token::addValue(const ValueFlow::Value &value) { if (value.isKnown() && _values) { // Clear all other values since value is known _values->clear(); } if (_values) { // Don't handle more than 10 values for performance reasons // TODO: add setting? if (_values->size() >= 10U) return false; // if value already exists, don't add it again std::list::iterator it; for (it = _values->begin(); it != _values->end(); ++it) { // different intvalue => continue if (it->intvalue != value.intvalue) continue; // different types => continue if (it->valueType != value.valueType) continue; if (value.isTokValue() && (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()) { *it = value; if (it->varId == 0) it->varId = _varId; break; } // Same value already exists, don't add new value return false; } // Add value if (it == _values->end()) { ValueFlow::Value v(value); if (v.varId == 0) v.varId = _varId; _values->push_back(v); } } else { ValueFlow::Value v(value); if (v.varId == 0) v.varId = _varId; _values = new std::list(1, v); } return true; } void Token::assignProgressValues(Token *tok) { unsigned int total_count = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) ++total_count; unsigned int count = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) tok2->_progressValue = count++ * 100 / total_count; } void Token::setValueType(ValueType *vt) { if (vt != valuetype) { delete valuetype; valuetype = vt; } } void Token::type(const ::Type *t) { _type = t; if (t) { tokType(eType); isEnumType(_type->isEnumType()); } else if (_tokType == eType) tokType(eName); } cppcheck-1.82/lib/token.h000066400000000000000000001011671322667425100153010ustar00rootroot00000000000000/* * 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 tokenH #define tokenH //--------------------------------------------------------------------------- #include "config.h" #include "mathlib.h" #include "valueflow.h" #include #include #include #include #include class Enumerator; class Function; class Scope; class Settings; class Type; class ValueType; class Variable; /// @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: Token **tokensBack; // Not implemented.. Token(); 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. eOther, eNone }; explicit Token(Token **tokens); ~Token(); template void str(T&& s) { _str = s; _varId = 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 _str; } /** * Unlink and delete the next 'index' tokens. */ void deleteNext(unsigned long index = 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[], unsigned int varid = 0); /** * @return length of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string **/ static std::size_t getStrLength(const Token *tok); /** * @return sizeof of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string **/ static std::size_t getStrSize(const Token *tok); /** * @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, std::size_t index); const ValueType *valueType() const { return valuetype; } void setValueType(ValueType *vt); const ValueType *argumentType() const { const Token *top = this; while (top && !Token::Match(top->astParent(), ",|(")) top = top->astParent(); return top ? top->valuetype : nullptr; } Token::Type tokType() const { return _tokType; } void tokType(Token::Type t) { _tokType = t; bool memoizedIsName = (_tokType == eName || _tokType == eType || _tokType == eVariable || _tokType == eFunction || _tokType == eKeyword || _tokType == eBoolean || _tokType == eEnumerator); // TODO: "true"/"false" aren't really a name... setFlag(fIsName, memoizedIsName); bool memoizedIsLiteral = (_tokType == eNumber || _tokType == eString || _tokType == eChar || _tokType == eBoolean || _tokType == eLiteral || _tokType == eEnumerator); setFlag(fIsLiteral, memoizedIsLiteral); } void isKeyword(bool kwd) { if (kwd) tokType(eKeyword); else if (_tokType == eKeyword) tokType(eName); } bool isKeyword() const { return _tokType == eKeyword; } bool isName() const { return getFlag(fIsName); } bool isUpperCaseName() const; bool isLiteral() const { return getFlag(fIsLiteral); } bool isNumber() const { return _tokType == eNumber; } bool isEnumerator() const { return _tokType == eEnumerator; } bool isOp() const { return (isConstOp() || isAssignmentOp() || _tokType == eIncDecOp); } bool isConstOp() const { return (isArithmeticalOp() || _tokType == eLogicalOp || _tokType == eComparisonOp || _tokType == eBitOp); } bool isExtendedOp() const { return isConstOp() || _tokType == eExtendedOp; } bool isArithmeticalOp() const { return _tokType == eArithmeticalOp; } bool isComparisonOp() const { return _tokType == eComparisonOp; } bool isAssignmentOp() const { return _tokType == eAssignmentOp; } bool isBoolean() const { return _tokType == eBoolean; } bool isUnaryPreOp() const; unsigned int flags() const { return _flags; } void flags(unsigned int flags_) { _flags = flags_; } bool isUnsigned() const { return getFlag(fIsUnsigned); } void isUnsigned(bool sign) { setFlag(fIsUnsigned, sign); } bool isSigned() const { return getFlag(fIsSigned); } void isSigned(bool sign) { setFlag(fIsSigned, sign); } bool isPointerCompare() const { return getFlag(fIsPointerCompare); } void isPointerCompare(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(bool b) { setFlag(fIsStandardType, b); } bool isExpandedMacro() const { return getFlag(fIsExpandedMacro); } void isExpandedMacro(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(bool ac) { setFlag(fIsAttributeConstructor, ac); } bool isAttributeDestructor() const { return getFlag(fIsAttributeDestructor); } void isAttributeDestructor(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(bool unused) { setFlag(fIsAttributeUsed, unused); } bool isAttributePure() const { return getFlag(fIsAttributePure); } void isAttributePure(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(bool value) { setFlag(fIsAttributeNoreturn, value); } bool isAttributeNothrow() const { return getFlag(fIsAttributeNothrow); } void isAttributeNothrow(bool value) { setFlag(fIsAttributeNothrow, value); } bool isAttributePacked() const { return getFlag(fIsAttributePacked); } void isAttributePacked(bool value) { setFlag(fIsAttributePacked, value); } bool isControlFlowKeyword() const { return getFlag(fIsControlFlowKeyword); } bool isOperatorKeyword() const { return getFlag(fIsOperatorKeyword); } void isOperatorKeyword(bool value) { setFlag(fIsOperatorKeyword, value); } bool isComplex() const { return getFlag(fIsComplex); } void isComplex(bool value) { setFlag(fIsComplex, value); } bool isEnumType() const { return getFlag(fIsEnumType); } void isEnumType(bool value) { setFlag(fIsEnumType, value); } bool isTemplateArg() const { return getFlag(fIsTemplateArg); } void isTemplateArg(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 unsigned int varId = 0U); static const Token *findmatch(const Token * const startTok, const char pattern[], const Token * const end, const unsigned int varId = 0U); 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 unsigned int varId = 0U) { return const_cast(findmatch(const_cast(startTok), pattern, varId)); } static Token *findmatch(Token * const startTok, const char pattern[], const Token * const end, const unsigned int varId = 0U) { 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, unsigned int varid); unsigned int fileIndex() const { return _fileIndex; } void fileIndex(unsigned int indexOfFile) { _fileIndex = indexOfFile; } unsigned int linenr() const { return _linenr; } void linenr(unsigned int lineNumber) { _linenr = lineNumber; } unsigned int col() const { return _col; } void col(unsigned int c) { _col = c; } Token *next() const { return _next; } /** * 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 _previous; } unsigned int varId() const { return _varId; } void varId(unsigned int id) { _varId = 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) { _link = linkToToken; if (_str == "<" || _str == ">") 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 _link; } /** * Associate this token with given scope * @param s Scope to be associated */ void scope(const Scope *s) { _scope = s; } /** * @return a pointer to the scope containing this token. */ const Scope *scope() const { return _scope; } /** * Associate this token with given function * @param f Function to be associated */ void function(const Function *f) { _function = f; if (f) tokType(eFunction); else if (_tokType == eFunction) tokType(eName); } /** * @return a pointer to the Function associated with this token. */ const Function *function() const { return _tokType == eFunction ? _function : nullptr; } /** * Associate this token with given variable * @param v Variable to be associated */ void variable(const Variable *v) { _variable = v; if (v || _varId) tokType(eVariable); else if (_tokType == eVariable) tokType(eName); } /** * @return a pointer to the variable associated with this token. */ const Variable *variable() const { return _tokType == eVariable ? _variable : 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 _tokType == eType ? _type : nullptr; } /** * @return a pointer to the Enumerator associated with this token. */ const Enumerator *enumerator() const { return _tokType == eEnumerator ? _enumerator : nullptr; } /** * Associate this token with given enumerator * @param e Enumerator to be associated */ void enumerator(const Enumerator *e) { _enumerator = e; if (e) tokType(eEnumerator); else if (_tokType == 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 */ unsigned int progressValue() const { return _progressValue; } /** 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(); /** * @return the original name. */ const std::string & originalName() const { return _originalName ? *_originalName : emptyString; } const std::list& values() const { return _values ? *_values : emptyValueList; } /** * Sets the original name. */ template void originalName(T&& name) { if (!_originalName) _originalName = new std::string(name); else *_originalName = name; } bool hasKnownIntValue() const { return _values && _values->size() == 1U && _values->front().isKnown() && _values->front().isIntValue(); } const ValueFlow::Value * getValue(const MathLib::bigint val) const { if (!_values) return nullptr; for (std::list::const_iterator it = _values->begin(); it != _values->end(); ++it) { if (it->isIntValue() && it->intvalue == val) return &(*it); } return nullptr; } const ValueFlow::Value * getMaxValue(bool condition) const { if (!_values) return nullptr; const ValueFlow::Value *ret = nullptr; for (std::list::const_iterator it = _values->begin(); it != _values->end(); ++it) { if (!it->isIntValue()) continue; if ((!ret || it->intvalue > ret->intvalue) && ((it->condition != nullptr) == condition)) ret = &(*it); } return ret; } const ValueFlow::Value * getMovedValue() const { if (!_values) return nullptr; for (std::list::const_iterator it = _values->begin(); it != _values->end(); ++it) { if (it->isMovedValue() && it->moveKind != ValueFlow::Value::NonMovedVariable) return &(*it); } return nullptr; } 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, unsigned int argnr, const Settings *settings) const; const Token *getValueTokenMaxStrLength() const; const Token *getValueTokenMinStrSize() const; const Token *getValueTokenDeadPointer() const; /** Add token value. Return true if value is added. */ bool addValue(const ValueFlow::Value &value); private: void next(Token *nextToken) { _next = nextToken; } void previous(Token *previousToken) { _previous = 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 _str; Token *_next; Token *_previous; Token *_link; // symbol database information const Scope *_scope; union { const Function *_function; const Variable *_variable; const ::Type* _type; const Enumerator *_enumerator; }; unsigned int _varId; unsigned int _fileIndex; unsigned int _linenr; unsigned int _col; /** * A value from 0-100 that provides a rough idea about where in the token * list this token is located. */ unsigned int _progressValue; Token::Type _tokType; 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), }; unsigned int _flags; /** * 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 ((_flags & 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_) { _flags = state_ ? _flags | flag_ : _flags & ~flag_; } /** Updates internal property cache like _isName or _isBoolean. Called after any _str() modification. */ void update_property_info(); /** Update internal property cache about isStandardType() */ void update_property_isStandardType(); // AST.. Token *_astOperand1; Token *_astOperand2; Token *_astParent; // original name like size_t std::string* _originalName; // ValueType ValueType *valuetype; // ValueFlow std::list* _values; static const std::list emptyValueList; public: void astOperand1(Token *tok); void astOperand2(Token *tok); const Token * astOperand1() const { return _astOperand1; } const Token * astOperand2() const { return _astOperand2; } const Token * astParent() const { return _astParent; } const Token *astTop() const { const Token *ret = this; while (ret->_astParent) ret = ret->_astParent; return ret; } /** * 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() { _astOperand1 = _astOperand2 = _astParent = nullptr; } void clearValueFlow() { delete _values; _values = nullptr; } std::string astString(const char *sep = "") const { std::string ret; if (_astOperand1) ret = _astOperand1->astString(sep); if (_astOperand2) ret += _astOperand2->astString(sep); return ret + sep + _str; } std::string astStringVerbose(const unsigned int indent1, const unsigned int indent2) const; std::string expressionString() const; void printAst(bool verbose, bool xml, std::ostream &out) const; void printValueFlow(bool xml, std::ostream &out) const; }; /// @} //--------------------------------------------------------------------------- #endif // tokenH cppcheck-1.82/lib/tokenize.cpp000077500000000000000000013452471322667425100163610ustar00rootroot00000000000000/* * 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 "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 //--------------------------------------------------------------------------- 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, unsigned int _startVarid) :isExecutable(_isExecutable), isStructInit(_isStructInit), isEnum(_isEnum), startVarid(_startVarid) { } const bool isExecutable; const bool isStructInit; const bool isEnum; const unsigned 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(); if (tok->isName()) tok = tok->next(); return (endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; } if (cpp && tok->str() == ")") { tok = tok->next(); while (Token::Match(tok, "const|noexcept|override|volatile|&|&& !!(") || (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) tok = tok->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->str() == ".") { // trailing return type for (tok = tok->next(); tok && !Token::Match(tok, "[;{]"); tok = tok->next()) if (tok->link() && Token::Match(tok, "<|[|(")) tok = tok->link(); } 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), _settings(nullptr), _errorLogger(nullptr), _symbolDatabase(nullptr), _varId(0), _codeWithTemplates(false), //is there any templates? m_timerResults(nullptr) #ifdef MAXTIME ,maxtime(std::time(0) + MAXTIME) #endif { } Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger) : list(settings), _settings(settings), _errorLogger(errorLogger), _symbolDatabase(nullptr), _varId(0), _codeWithTemplates(false), //is there any templates? m_timerResults(nullptr) #ifdef MAXTIME ,maxtime(std::time(0) + MAXTIME) #endif { // make sure settings are specified assert(_settings); } Tokenizer::~Tokenizer() { delete _symbolDatabase; } //--------------------------------------------------------------------------- // SizeOfType - gives the size of a type //--------------------------------------------------------------------------- unsigned int Tokenizer::sizeOfType(const Token *type) const { if (!type || type->str().empty()) return 0; if (type->tokType() == Token::eString) return Token::getStrLength(type) + 1U; std::map::const_iterator it = _typeSize.find(type->str()); if (it == _typeSize.end()) { const Library::PodType* podtype = _settings->library.podtype(type->str()); if (!podtype) return 0; return podtype->size; } else if (type->isLong()) { if (type->str() == "double") return _settings->sizeof_long_double; else if (type->str() == "long") return _settings->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().find("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 (!_settings->debugwarnings) return; std::ostringstream str; const Token *tok1 = tok; unsigned 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() : classEnd(nullptr), isNamespace(false) { } std::string className; const Token * classEnd; bool isNamespace; }; } static Token *splitDefinitionFromTypedef(Token *tok) { 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 static unsigned int count = 0; name = "Unnamed" + MathLib::toString(count++); } 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 (_errorLogger && !list.getFiles().empty()) _errorLogger->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.classEnd = tok->link(); spaceInfo.push_back(info); hasClass = false; } else if (!spaceInfo.empty() && tok->str() == "}" && spaceInfo.back().classEnd == 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); 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); 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 functionRef = 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(); 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.push_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(); unsigned int scope = 0; bool simplifyType = false; bool inMemberFunc = false; int memberScope = 0; bool globalScope = false; std::size_t 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].classEnd) { --classLevel; pattern.clear(); for (std::size_t 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 *func = tok2->link()->previous(); 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].classEnd = tok2->link(); ++classLevel; pattern.clear(); for (std::size_t 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; std::size_t count = 0; int back = int(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 (std::size_t i = classLevel; i < spaceInfo.size(); ++i) { tok2->deleteNext(2); } simplifyType = true; } } else { if (tok2->strAt(-1) == "::") { std::size_t 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(), "> ("))) 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 (typeStart->str() == "struct" && 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 (std::size_t 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 (std::list::const_iterator iter = pointers.begin(); iter != pointers.end(); ++iter) { tok2->insertToken(*iter); 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 || functionRef || 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(); } else if (functionRef) { 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(); // skip over name if (tok2->next() && tok2->next()->str() != ")") { 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(); } else { // syntax error } tok2->insertToken(")"); Token::createMutualLinks(tok2->next(), tok3); } 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 tok2 = tok2->next(); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, openParenthesis); } } } else if (typeOf) { tok2 = TokenList::copyTokens(tok2, argStart, argEnd); } else if (tok2->strAt(2) == "[") { do { if (!tok2->linkAt(2)) syntaxError(tok2); tok2 = tok2->linkAt(2)->previous(); } while (tok2->strAt(2) == "["); } if (arrayStart && arrayEnd) { do { if (!tok2->next()) syntaxError(tok2); // can't recover so quit if (!inCast && !inSizeof) tok2 = tok2->next(); // reference to array? if (tok2->str() == "&") { tok2 = tok2->previous(); tok2->insertToken("("); Token *tok3 = tok2->next(); // handle missing variable name if (tok2->strAt(3) == ")" || tok2->strAt(3) == ",") tok2 = tok2->tokAt(2); else tok2 = tok2->tokAt(3); if (!tok2) syntaxError(nullptr); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } if (!tok2->next()) syntaxError(tok2); // can't recover so quit tok2 = TokenList::copyTokens(tok2, arrayStart, arrayEnd); if (!tok2->next()) syntaxError(tok2); tok2 = tok2->next(); 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; } } } } 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 unsigned 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 unsigned 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(_settings); return list.createTokens(code, Path::getRelativePath(Path::simplifyPath(FileName), _settings->basePaths)); } void Tokenizer::createTokens(const simplecpp::TokenList *tokenList) { // make sure settings specified assert(_settings); list.createTokens(tokenList); } bool Tokenizer::simplifyTokens1(const std::string &configuration) { // Fill the map _typeSize.. fillTypeSizes(); _configuration = configuration; if (!simplifyTokenList1(list.getFiles().front().c_str())) return false; list.createAst(); list.validateAst(); createSymbolDatabase(); // Use symbol database to identify rvalue references. Split && to & &. This is safe, since it doesn't delete any tokens (which might be referenced by symbol database) for (std::size_t i = 0; i < _symbolDatabase->getVariableListSize(); i++) { const Variable* var = _symbolDatabase->getVariableFromVarId(i); if (var && var->isRValueReference()) { Token* endTok = const_cast(var->typeEndToken()); endTok->str("&"); endTok->astOperand1(nullptr); endTok->astOperand2(nullptr); endTok->insertToken("&"); endTok->next()->scope(endTok->scope()); } } _symbolDatabase->setValueTypeInTokenList(); ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings); 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(); TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates(list.front()); } 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() { _typeSize.clear(); _typeSize["char"] = 1; _typeSize["_Bool"] = _settings->sizeof_bool; _typeSize["bool"] = _settings->sizeof_bool; _typeSize["short"] = _settings->sizeof_short; _typeSize["int"] = _settings->sizeof_int; _typeSize["long"] = _settings->sizeof_long; _typeSize["float"] = _settings->sizeof_float; _typeSize["double"] = _settings->sizeof_double; _typeSize["wchar_t"] = _settings->sizeof_wchar_t; _typeSize["size_t"] = _settings->sizeof_size_t; _typeSize["*"] = _settings->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))) { 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; unsigned int par = 0U; 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% )")) { t->deleteThis(); t->deleteThis(); t->deleteNext(); } tok->str("."); tok->originalName("->"); } } } void Tokenizer::combineStringAndCharLiterals() { // Combine wide strings and wide characters for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[Lu] %char%|%str%")) { // Combine 'L "string"' and 'L 'c'' tok->str(tok->next()->str()); tok->deleteNext(); tok->isLong(true); } } // Combine strings for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str()[0] != '"') 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->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; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "extern \"C\"")) { if (tok->strAt(2) == "{") { tok->linkAt(2)->deleteThis(); tok->deleteNext(2); } else tok->deleteNext(); tok->deleteThis(); } } } 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(); std::size_t sz = Token::getStrSize(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) { 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() && 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; unsigned 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")) { 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% :")) { MathLib::bigint start = MathLib::toLongNumber(tok->strAt(1)); MathLib::bigint end = MathLib::toLongNumber(tok->strAt(5)); end = std::min(start + 50, end); // Simplify it 50 times at maximum if (start < end) { tok = tok->tokAt(2); tok->str(":"); tok->deleteNext(); tok->next()->str("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% :")) { char start = tok->strAt(1)[1]; char end = tok->strAt(5)[1]; if (start < end) { tok = tok->tokAt(2); tok->str(":"); tok->deleteNext(); tok->next()->str("case"); for (char i = end - 1; i > start; i--) { tok->insertToken(":"); tok->insertToken(std::string(1, '\'') + i + '\''); tok->insertToken("case"); } } } } } void Tokenizer::simplifyTemplates() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // simple fix for sizeof used as template parameter // TODO: this is a bit hardcoded. make a bit more generic if (Token::Match(tok, "%name% < sizeof ( %type% ) >") && tok->tokAt(4)->isStandardType()) { Token * const tok3 = tok->next(); const unsigned int sizeOfResult = sizeOfType(tok3->tokAt(3)); tok3->deleteNext(4); tok3->insertToken(MathLib::toString(sizeOfResult)); } // Ticket #6181: normalize C++11 template parameter list closing syntax if (tok->str() == "<" && TemplateSimplifier::templateParameters(tok)) { Token *endTok = tok->findClosingBracket(); if (endTok && endTok->str() == ">>") { endTok->str(">"); endTok->insertToken(">"); } } } TemplateSimplifier::simplifyTemplates( list, _errorLogger, _settings, #ifdef MAXTIME maxtime, #else 0, // ignored #endif _codeWithTemplates); } //--------------------------------------------------------------------------- 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; unsigned int typeCount = 0; unsigned 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 * tok3 = tok2->findClosingBracket(); if (tok3 == nullptr) { /* Ticket #8151 */ throw tok2; } tok2 = tok3; if (tok2->str() != ">") break; singleNameCount = 1; } 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() != "::") { break; } tok2 = tok2->next(); } if (tok2) { *tok = tok2; // In executable scopes, references must be assigned // Catching by reference is an exception if (executableScope && ref) { 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%")); } static void setVarIdStructMembers(Token **tok1, std::map >& structMembers, unsigned int *_varId) { Token *tok = *tok1; if (Token::Match(tok, "%name% = { . %name% =")) { const unsigned 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% !!(")) { const unsigned int struct_varid = tok->varId(); tok = tok->tokAt(2); if (struct_varid == 0) continue; // 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 std::map &variableId, const unsigned 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.. unsigned 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 = variableId.find(tok->str()); if (it != variableId.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 = variableId.find(tok->str()); if (it != variableId.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, &_varId); } } } } else if (indentlevel == 0 && tok->str() == ":" && !initListArgLastToken) initList = true; } } // Update the variable ids.. // Parse each function.. static void setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, const std::map &varlist, std::map >& structMembers, unsigned 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. static const std::set notstart_c = make_container< std::set > () << "goto" << "NOT" << "return" << "sizeof"<< "typedef"; static const std::set notstart_cpp = make_container< std::set > () << 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; // variable id _varId = 0; std::map variableId; std::map > structMembers; std::stack< std::map > scopeInfo; std::stack scopeStack; scopeStack.push(VarIdScopeInfo()); std::stack functionDeclEndStack; bool initlist = false; bool inlineFunction = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!functionDeclEndStack.empty() && tok == functionDeclEndStack.top()) { functionDeclEndStack.pop(); if (tok->str() == ":") initlist = true; else if (tok->str() == ";") { if (scopeInfo.empty()) cppcheckError(tok); variableId.swap(scopeInfo.top()); scopeInfo.pop(); } else if (tok->str() == "{") scopeStack.push(VarIdScopeInfo(true, scopeStack.top().isStructInit || tok->strAt(-1) == "=", /*isEnum=*/false, _varId)); } 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 && (functionDeclEndStack.empty() || newFunctionDeclEnd != functionDeclEndStack.top())) { functionDeclEndStack.push(newFunctionDeclEnd); scopeInfo.push(variableId); } } 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; if (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) == "=")) scopeInfo.push(variableId); } initlist = false; scopeStack.push(VarIdScopeInfo(isExecutable, scopeStack.top().isStructInit || tok->strAt(-1) == "=", isEnumStart(tok), _varId)); } 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(), variableId, scopeStack.top().startVarid, structMembers); } if (!scopeStack.top().isStructInit) { if (scopeInfo.empty()) { variableId.clear(); } else { variableId.swap(scopeInfo.top()); scopeInfo.pop(); } } 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, variableId, 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() && (variableId.find(tok3->str()) != variableId.end())) || 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) { variableId[prev2->str()] = ++_varId; // 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() && variableId.find(tok->str()) != variableId.end()) tok->varId(variableId[tok->str()]); 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; } // 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() > 0U) 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 = variableId.find(tok->str()); if (it != variableId.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, &_varId); } } } 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(); } } } 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; }; struct ScopeInfo2 { ScopeInfo2(const std::string &name_, const Token *classEnd_) : name(name_), classEnd(classEnd_) {} const std::string name; const Token * const classEnd; }; } static std::string getScopeName(const std::list &scopeInfo) { std::string ret; for (std::list::const_iterator it = scopeInfo.begin(); it != scopeInfo.end(); ++it) ret += (ret.empty() ? "" : " :: ") + (it->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 (std::list::const_iterator ns = member.usingnamespaces.begin(); ns != member.usingnamespaces.end(); ++ns) { 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% ::|;")) { const 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)); } else if (Token::Match(tok, "namespace %name% {")) { scope.push_back(tok->strAt(1)); endOfScope[tok->linkAt(2)] = tok->strAt(1); } } if (tok->str() == "}") { 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.push_back(Member(scope, usingnamespaces, tok1)); else allMemberVars.push_back(Member(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().classEnd) 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 (std::list::const_iterator it = classnameTokens.begin(); it != classnameTokens.end(); ++it) 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 (std::list::const_iterator it = classnameTokens.begin(); it != classnameTokens.end(); ++it) scopeInfo.push_back(ScopeInfo2((*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, &_varId); tok2 = tok2->link(); } else if (tok2->str() == "(" && tok2->link()->strAt(1) != "(") tok2 = tok2->link(); } // 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 (std::list::iterator var = allMemberVars.begin(); var != allMemberVars.end(); ++var) { 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 (std::list::const_iterator func = allMemberFunctions.begin(); func != allMemberFunctions.end(); ++func) { 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, &_varId); } 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 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, &_varId); } } } } 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->syntaxError(token, open); } if (type.top()->str()[0] != open) { tokenizer->syntaxError(type.top(), type.top()->str()[0]); } 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. syntaxError(links1.top(), '{'); } if (!links2.empty()) { // Error, ( and ) don't match. syntaxError(links2.top(), '('); } if (!links3.empty()) { // Error, [ and ] don't match. syntaxError(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() == "<") type.pop(); type.pop(); templateToken = nullptr; } 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() == "<") type.pop(); } else if (token->str() == "<" && token->previous() && token->previous()->isName() && !token->previous()->varId()) { 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 (token->str() == ">>" && (!top2 || top2->str() != "<")) continue; if (token->next() && !Token::Match(token->next(), "%name%|>|&|&&|*|::|,|(|)|{|}|;|[|:") && !Token::Match(token->next(), "&& %name% =")) continue; // if > is followed by [ .. "new a[" is expected if (token->strAt(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(); 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 unsigned 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 unsigned 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% ] [[;=]")) { unsigned 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 *= (unsigned)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(nullptr); 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); } // sizeof('x') if (Token::Match(tok->next(), "( %char% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream sz; sz << ((isC()) ? _settings->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% )")) { 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 unsigned 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% [")) { unsigned 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 unsigned 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 (unsigned int i = 0; i < derefs; i++) tok2 = tok2->linkAt(1); // Skip all dimensions that are derefenced 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 { 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(); // Remove __asm.. simplifyAsm(); // Bail out if code is garbage if (const Token *garbage = findGarbageCode()) syntaxError(garbage); 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; // Remove [[deprecated]] simplifyDeprecated(); // remove __attribute__((?)) simplifyAttribute(); // 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 "volatile", "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->previous()); } 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() && !_settings->library.markupFile(FileName)) { findComplicatedSyntaxErrorsInTemplates(); } if (_settings->terminated()) return false; // remove calling conventions __cdecl, __stdcall.. simplifyCallingConvention(); // Remove __declspec() simplifyDeclspec(); validate(); // 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 (m_timerResults) { Timer t("Tokenizer::tokenize::simplifyTypedef", _settings->showtime, m_timerResults); simplifyTypedef(); } else { simplifyTypedef(); } // 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() && _settings->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(); // 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 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 (!isC()) { // TODO: Only simplify template parameters for (Token *tok = list.front(); tok; tok = tok->next()) while (TemplateSimplifier::simplifyNumericCalculations(tok)) ; // Handle templates.. 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 TemplateSimplifier::cleanupAfterSimplify(list.front()); } // 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 (m_timerResults) { Timer t("Tokenizer::tokenize::setVarId", _settings->showtime, m_timerResults); 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(); // Convert e.g. atol("0") into 0 simplifyMathFunctions(); simplifyDoublePlusAndDoubleMinus(); simplifyArrayAccessSyntax(); Token::assignProgressValues(list.front()); removeRedundantSemicolons(); simplifyParameterVoid(); simplifyRedundantConsecutiveBraces(); simplifyEmptyNamespaces(); elseif(); SimplifyNamelessRValueReferences(); validate(); 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(); } // 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(); _symbolDatabase->setValueTypeInTokenList(); ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings); if (_settings->terminated()) return false; printDebugOutput(2); return true; } //--------------------------------------------------------------------------- void Tokenizer::printDebugOutput(unsigned int simplification) const { const bool debug = (simplification != 1U && _settings->debug) || (simplification != 2U && _settings->debugnormal); if (debug && list.front()) { list.front()->printOut(nullptr, list.getFiles()); if (_settings->xml) std::cout << "" << std::endl; if (_symbolDatabase) { if (_settings->xml) _symbolDatabase->printXml(std::cout); else if (_settings->verbose) { _symbolDatabase->printOut("Symbol database"); } } if (_settings->verbose) list.front()->printAst(_settings->verbose, _settings->xml, std::cout); list.front()->printValueFlow(_settings->xml, std::cout); if (_settings->xml) std::cout << "" << std::endl; } if (_symbolDatabase && simplification == 2U && _settings->debugwarnings) { printUnknownTypes(); // the typeStartToken() should come before typeEndToken() for (unsigned int varid = 1; varid < _symbolDatabase->getVariableListSize(); varid++) { const Variable *var = _symbolDatabase->getVariableFromVarId(varid); 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() << '\"'; 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->link()) out << " link=\"" << tok->link() << '\"'; 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->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; _symbolDatabase->printXml(out); if (list.front()) list.front()->printValueFlow(true, out); } 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")) tok->deleteNext(); } if ((!tok->previous() || Token::Match(tok->previous(), "[;{}]")) && Token::Match(tok, "%type%") && tok->isUpperCaseName()) { const Token *tok2 = tok->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); // 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::removeRedundantAssignment() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); Token * 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)); unsigned 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% (") && _settings->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(); 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); bool type = varTok->next()->isName(); if (type) varTok = varTok->next(); const std::string varname(varTok->str()); const unsigned 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") { 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; // 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); unsigned int ternaryOplevel = 0; for (const Token *endTok = colon; endTok; endTok = endTok->next()) { if (Token::Match(endTok, "(|[|{")) { endTok = endTok->link(); } 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(); unsigned 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()); unsigned int bits = _settings->char_bit * _typeSize[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) == ",")) { // 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(); std::map::iterator it; for (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 tok->next()->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| ;|,|)|=|[|{")) continue; if (endTok->strAt(1) == "const") 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() { bool ret = false; 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); const std::string pattern("(|[|=|return|%op% " + tok->str() + " ( ) ;|]|)|%cop%"); for (Token *tok2 = list.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, pattern.c_str())) { if (tok->str() != tok2->strAt(1)) // Ticket #7916: tok is for instance "foo < bar >", a single token for an instantiation, // and tok2->strAt(1) is "foo"; bail out (TODO: we can probably handle this pattern) continue; tok2 = tok2->next(); tok2->str(any->str()); tok2->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 = _settings->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; bool isconst = false; bool isstatic = false; Token *tok2 = type0; unsigned 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; unsigned int indentlevel = 0; unsigned 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 <= 1U) { 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(); if (!isC() && tok2->str() == "<" && TemplateSimplifier::templateParameters(tok2) > 0) { tok2 = tok2->findClosingBracket(); } if (!tok2) syntaxError(nullptr); // #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? list.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((int)typelen); while (Token::Match(varTok, "*|&|const")) varTok = varTok->next(); if (!varTok) syntaxError(tok2); // invalid code list.insertTokens(eq, varTok, 2); eq->str(";"); // "= x, " => "= x; type " if (tok2->str() == ",") { tok2->str(";"); list.insertTokens(tok2, type0, typelen); } break; } if (tok2) tok2 = tok2->next(); } } finishedwithkr = (only_k_r_fpar && tok2 && tok2->strAt(1) == "{"); } } void Tokenizer::simplifyPlatformTypes() { const bool isCPP11 = _settings->standards.cpp >= Standards::CPP11; enum { isLongLong, isLong, isInt } type; /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ if (_settings->sizeof_size_t == _settings->sizeof_long) type = isLong; else if (_settings->sizeof_size_t == _settings->sizeof_long_long) type = isLongLong; else if (_settings->sizeof_size_t == _settings->sizeof_int) type = isInt; else return; for (Token *tok = list.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(_settings->platformString()); for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) continue; const Library::PlatformType * const platformtype = _settings->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->_const_ptr) { tok->str("const"); tok->insertToken("*"); tok->insertToken(platformtype->_type); typeToken = tok; } else if (platformtype->_pointer) { tok->str(platformtype->_type); typeToken = tok; tok->insertToken("*"); } else if (platformtype->_ptr_ptr) { tok->str(platformtype->_type); typeToken = tok; tok->insertToken("*"); tok->insertToken("*"); } else { tok->originalName(tok->str()); tok->str(platformtype->_type); typeToken = tok; } if (platformtype->_signed) typeToken->isSigned(true); if (platformtype->_unsigned) typeToken->isUnsigned(true); if (platformtype->_long) typeToken->isLong(true); } } } void Tokenizer::simplifyStaticConst() { // This function will simplify the token list so that the qualifiers "extern", "static" // and "const" appear in the reverse order to what is in the array below. const std::string qualifiers[] = {"const", "static", "extern"}; // Move 'const' before all other qualifiers and types and then // move 'static' before all other qualifiers and types, ... for (size_t i = 0; i < sizeof(qualifiers)/sizeof(qualifiers[0]); i++) { for (Token *tok = list.front(); tok; tok = tok->next()) { // 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; for (; leftTok; leftTok = leftTok->previous()) { if (!Token::Match(leftTok, "%type%|static|const|extern|struct|::") || (isCPP() && Token::Match(leftTok, "private:|protected:|public:|operator"))) break; } // The token preceding the declaration should indicate the start of a declaration if (leftTok == tok || (leftTok && !Token::Match(leftTok, ";|{|}|(|,|private:|protected:|public:"))) { continue; } // 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(); } else if (leftTok->next()) leftTok->next()->insertToken(qualifiers[i], emptyString, true); else leftTok->insertToken(qualifiers[i]); } } } 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) { unsigned 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 = make_container< std::map >() << 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 */ unsigned 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; 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 unsigned int numTokens = (Token::Match(tok, "class|struct|union")) ? 2U : 1U; list.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::map constantValues; 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; const 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% )| ;")) { //check if there's not a reference usage inside the code bool withreference = false; for (const Token *tok2 = valuetok->tokAt(2); tok2; tok2 = tok2->next()) { if (Token::Match(tok2,"(|[|,|{|return|%op% & %varid%", vartok->varId())) { withreference = true; break; } } //don't simplify 'f(&x)' to 'f(&100)' if (withreference) continue; constantValues[vartok->varId()] = valuetok->str(); // remove statement while (tok1->next()->str() != ";") tok1->deleteNext(); tok1->deleteNext(); tok1->deleteThis(); tok = tok1; goback = true; ret = true; } } else if (tok->varId() && constantValues.find(tok->varId()) != constantValues.end()) { tok->str(constantValues[tok->varId()]); } } } // 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 unsigned 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; unsigned 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 unsigned 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 unsigned 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(unsigned int varid, Token **_tok2, Token **_tok3, std::string &value, unsigned 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, unsigned int varid, const std::string &structname, std::string &value, unsigned 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 = _settings->debugwarnings; if (_errorLogger && !list.getFiles().empty()) _errorLogger->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 (unsigned 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 (unsigned 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) && (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 (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; continue; } 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::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 unsigned 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 TemplateSimplifier::simplifyCalculations(list.front()); } 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 = make_container< std::set > () << "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 = _settings->library.isScopeNoReturn(endScopeToken,&unknownFunc); if (unknown) *unknown = !unknownFunc.empty(); if (!unknownFunc.empty() && _settings->checkLibrary && _settings->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? unsigned 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); unsigned 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% ;"); unsigned int indentlevel = 1, indentcase = 0, indentswitch = 0, indentlabel = 0, roundbraces = 0, 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); unsigned 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 { printDebugOutput(0); throw InternalError(tok, "syntax error", InternalError::SYNTAX); } void Tokenizer::syntaxError(const Token *tok, char c) const { printDebugOutput(0); if (_configuration.empty()) throw InternalError(tok, std::string("Invalid number of character '") + c + "' when no macros are defined.", InternalError::SYNTAX); else throw InternalError(tok, std::string("Invalid number of character '") + c + "' when these macros are defined: '" + _configuration + "'.", 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::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 following functions: atol(), abs(), fabs() // labs(), llabs(), fmin(), fminl(), fminf(), fmax(), fmaxl() // fmaxf(), isgreater(), isgreaterequal(), isless() // islessgreater(), islessequal(), pow(), powf(), powl(), // div(),ldiv(),lldiv(), 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(), fma() // 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; std::size_t 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 (!_settings->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); } const Token * Tokenizer::findGarbageCode() const { 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%|:|;|{|}|(|)|,")) return tok; if (Token::Match(tok->previous(), "[(,]")) continue; if (!Token::Match(tok->next(), "( !!)")) return tok; if (tok->str() != "for") { if (isGarbageExpr(tok->next(), tok->linkAt(1))) return tok; } } } // case keyword must be inside switch for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "switch (") && Token::simpleMatch(tok->linkAt(1), ") {")) tok = tok->linkAt(1)->linkAt(1); else if (tok->str() == "(") tok = tok->link(); else if (tok->str() == "case") return tok; } for (const Token *tok = tokens(); tok ; tok = tok->next()) { if (!Token::simpleMatch(tok, "for (")) // find for loops continue; // count number of semicolons unsigned 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)) { return tok; } } // Operators without operands.. for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%cop% %or%|%oror%|/|%")) return tok; if (Token::Match(tok, ";|(|[ %comp%")) return tok; } // Code must not start with an arithmetical operand if (Token::Match(list.front(), "%cop%")) return list.front(); // Code must end with } ; ) NAME if (!Token::Match(list.back(), "%name%|;|}|)")) return list.back(); if (list.back()->str() == ")" && !Token::Match(list.back()->link()->previous(), "%name% (")) return list.back(); if (Token::Match(list.back(), "void|char|short|int|long|float|double|const|volatile|static|inline|struct|class|enum|union|template|sizeof|case|break|continue|typedef")) return list.back(); if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword()) return 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(), "[:;{})]")) return tok; const Token * const tok1 = tok; tok = tok->next()->findClosingBracket(); if (!tok) return tok1; if (!Token::Match(tok, ">|>> %name%")) return tok->next() ? tok->next() : tok1; } } return nullptr; } bool Tokenizer::isGarbageExpr(const Token *start, const Token *end) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->isControlFlowKeyword()) return true; if (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 unsigned 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() { unsigned 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); Token *var = tok->tokAt(4); Token *end = tok->next()->link()->next()->link(); const unsigned int varid = ++_varId; // 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. unsigned 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(), "} *|&| %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(), "} *|&| %type% ,|;|[|(|{")) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } } // check for anonymous enum else if ((Token::simpleMatch(tok, "enum {") && Token::Match(tok->next()->link(), "} %type%| ,|;|[|(|{")) || (Token::Match(tok, "enum : %type% {") && Token::Match(tok->linkAt(3), "} %type%| ,|;|[|(|{"))) { 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(); Token *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(), "*|&| %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); // 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")) { 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() { bool windows = _settings->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% (") && !_settings->library.isNotLibraryFunction(tok)) { if (_settings->library.isFunctionConst(tok->str(), true)) tok->isAttributePure(true); if (_settings->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__ )")) { 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 (Token::simpleMatch(tok->previous(), "} __attribute__ ( ( packed )")) { tok->previous()->isAttributePacked(true); } Token::eraseTokens(tok, tok->next()->link()->next()); tok->deleteThis(); } } } static const std::set keywords = make_container< std::set >() << "volatile" << "inline" << "_inline" << "__inline" << "__forceinline" << "register" << "__restrict" << "__restrict__" ; // Remove "volatile", "inline", "register", "restrict", "override", "final", "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. for (Token *tok = list.front(); tok; tok = tok->next()) { if (keywords.find(tok->str()) == keywords.end()) continue; // Don't remove struct members if (Token::simpleMatch(tok->previous(), ".")) continue; // Simplify.. tok->deleteThis(); } if (isC() || _settings->standards.cpp == Standards::CPP03) { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "auto") tok->deleteThis(); } } if (_settings->standards.c >= Standards::C99) { for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "restrict") { tok->deleteThis(); } // simplify static keyword: // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); if (Token::Match(tok, "[ static %num%")) { tok->deleteNext(); } } } if (_settings->standards.c >= Standards::C11) { for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "_Atomic") { tok->deleteThis(); } } } if (isCPP() && _settings->standards.cpp >= Standards::CPP11) { for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "constexpr") { tok->deleteThis(); } // final: // 1) struct name final { }; <- struct is final if (Token::Match(tok, "%type% final [:{]")) { tok->deleteNext(); continue; } // final: // 2) void f() final; <- function is final if (Token::Match(tok, ") const|override|final")) { Token* specifier = tok->tokAt(2); while (Token::Match(specifier, "const|override|final")) { specifier=specifier->next(); } if (Token::Match(specifier, "[{;]")) { specifier = tok->next(); while (!Token::Match(specifier, "[{;]")) { if (specifier->str()=="final") specifier->deleteThis(); else specifier=specifier->next(); } } } // noexcept -> noexcept(true) // 3) void f() noexcept; -> void f() noexcept(true); 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); } } } } 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 "} )" unsigned 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; unsigned 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 unsigned 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), ") {")) { 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); } } } // When the assembly code has been cleaned up, no @ is allowed for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") { tok = tok->link(); if (!tok) syntaxError(nullptr); } else if (tok->str()[0] == '@') { syntaxError(nullptr); } } } // 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 (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(";"); Token *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 = make_container >() << "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 = make_container >() << "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 = make_container >() << "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 = _settings->standards.cpp == Standards::CPP11; 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% (") && !Token::Match(tok->linkAt(1)->next(), "%name%|{") && 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 (!_settings->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 = make_container< std::map >() << 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 (!_settings->isWindowsPlatform()) return; const bool ansi = _settings->platformType == Settings::Win32A; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->strAt(1) != "(") continue; 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 ( %char%|%str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); if (!ansi) tok->isLong(true); while (Token::Match(tok->next(), "_T ( %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 (!_settings->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 unsigned 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 (!_symbolDatabase) _symbolDatabase = new SymbolDatabase(this, _settings, _errorLogger); _symbolDatabase->validate(); } void Tokenizer::deleteSymbolDatabase() { delete _symbolDatabase; _symbolDatabase = nullptr; } static bool operatorEnd(const Token * tok) { if (tok && tok->str() == ")") { tok = tok->next(); while (tok && !Token::Match(tok, "[=;{),]")) { if (Token::Match(tok, "const|volatile")) { 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 return false; } return true; } return false; } void Tokenizer::simplifyOperatorName() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "operator") { // 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; } if (Token::Match(par, ".|%op%|,")) { op += par->str(); par = par->next(); done = false; } if (Token::simpleMatch(par, "[ ]")) { op += "[]"; par = par->tokAt(2); done = false; } 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; } } if (par && operatorEnd(par->link())) { tok->str("operator" + op); Token::eraseTokens(tok, par); } if (!op.empty()) tok->isOperatorKeyword(true); } } if (_settings->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.classEnd = tok->link(); classInfo.push_back(info); } else if (!classInfo.empty()) { if (tok == classInfo.back().classEnd) 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::simplifyDeprecated() { if (_settings->standards.cpp != Standards::CPP11 || isC()) return; // It is actually a C++14 feature, however, there seems to be nothing dangerous about removing it for C++11 as well for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->link() && Token::simpleMatch(tok, "[ [ deprecated")) { Token::eraseTokens(tok, tok->link()->next()); tok->deleteThis(); } } } 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 (unsigned char 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 ',' Token *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 (!_symbolDatabase) return; std::multimap unknowns; for (unsigned int i = 1; i <= _varId; ++i) { const Variable *var = _symbolDatabase->getVariableFromVarId(i); // is unknown type? if (var && !var->type() && !var->typeStartToken()->isStandardType()) { 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::multimap::const_iterator it; std::string last; size_t count = 0; for (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 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 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 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 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; unsigned 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() == "?") { 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 (_errorLogger) _errorLogger->reportErr(errmsg); else Check::reportError(errmsg); } void Tokenizer::setPodTypes() { if (!_settings) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; // pod type const struct Library::PodType *podType = _settings->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); } } } void Tokenizer::SimplifyNamelessRValueReferences() { // Simplify nameless rValue references - named ones are simplified later for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "&& [,)]")) { tok->str("&"); tok->insertToken("&"); } } } 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; } cppcheck-1.82/lib/tokenize.h000066400000000000000000000575251322667425100160210ustar00rootroot00000000000000/* * 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 tokenizeH #define tokenizeH //--------------------------------------------------------------------------- #include "config.h" #include "errorlogger.h" #include "tokenlist.h" #include #include #include #include class Settings; class SymbolDatabase; class TimerResults; class Token; 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 TestTokenizer; friend class SymbolDatabase; public: Tokenizer(); Tokenizer(const Settings * settings, ErrorLogger *errorLogger); ~Tokenizer(); void setTimerResults(TimerResults *tr) { m_timerResults = 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[]); void SimplifyNamelessRValueReferences(); /** * Most aggressive simplification of tokenlist * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList2(); /** * 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. */ unsigned 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(); /** 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); /** * Convert platform dependent types to standard types. * 32 bits: size_t -> unsigned long * 64 bits: size_t -> unsigned long long */ void simplifyPlatformTypes(); /** * 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(); /** * 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(unsigned int varid, Token **_tok2, Token **_tok3, std::string &value, unsigned int &valueVarId, bool &valueIsPointer, bool floatvar); /** * utility function for simplifyKnownVariables. Perform simplification * of a given variable */ bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsigned int varid, const std::string &structname, std::string &value, unsigned 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 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; /** Syntax error. Example: invalid number of ')' */ void syntaxError(const Token *tok, char c) const; /** Syntax error. C++ code in C file. */ void syntaxErrorC(const Token *tok, const std::string &what) 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 */ const Token * findGarbageCode() const; /** Detect garbage expression */ static bool isGarbageExpr(const Token *start, const Token *end); /** * Remove __declspec() */ void simplifyDeclspec(); /** * Remove calling convention */ void simplifyCallingConvention(); /** * Remove __attribute__ ((?)) */ void simplifyAttribute(); /** * Remove keywords "volatile", "inline", "register", and "restrict" */ void simplifyKeyword(); /** * Remove __asm */ void simplifyAsm(); /** * asm heuristics, Put ^{} statements in asm() */ void simplifyAsm2(); /** * 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 [[deprecated]] (C++14) from TokenList */ void simplifyDeprecated(); /** * Replace strlen(str) * @return true if any replacement took place, false else * */ bool simplifyStrlen(); /** * 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 std::map &variableId, const unsigned int scopeStartVarId, std::map >& structMembers); /** * 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); public: /** Was there templates in the code? */ bool codeWithTemplates() const { return _codeWithTemplates; } void setSettings(const Settings *settings) { _settings = settings; list.setSettings(settings); } const SymbolDatabase *getSymbolDatabase() const { return _symbolDatabase; } 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(unsigned int simplification) const; void dump(std::ostream &out) const; Token *deleteInvalidTypedef(Token *typeDef); /** * Get variable count. * @return number of variables */ unsigned int varIdCount() const { return _varId; } /** * 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) > maxtime); #else static bool isMaxTime() { return false; #endif } private: /** Disable copy constructor, no implementation */ Tokenizer(const Tokenizer &); /** Disable assignment operator, no implementation */ Tokenizer &operator=(const Tokenizer &); Token *processFunc(Token *tok2, bool inOperator) const; /** * Get new variable id. * @return new variable id */ unsigned int newVarId() { return ++_varId; } /** Set pod types */ void setPodTypes(); /** settings */ const Settings * _settings; /** errorlogger */ ErrorLogger* const _errorLogger; /** Symbol database that all checks etc can use */ SymbolDatabase *_symbolDatabase; /** E.g. "A" for code where "#ifdef A" is true. This is used to print additional information in error situations. */ std::string _configuration; /** sizeof information for known types */ std::map _typeSize; /** variable count */ unsigned int _varId; /** * was there any templates? templates that are "unused" are * removed from the token list */ bool _codeWithTemplates; /** * TimerResults */ TimerResults *m_timerResults; #ifdef MAXTIME /** Tokenizer maxtime */ std::time_t maxtime; #endif }; /// @} //--------------------------------------------------------------------------- #endif // tokenizeH cppcheck-1.82/lib/tokenlist.cpp000066400000000000000000001270751322667425100165360ustar00rootroot00000000000000/* * 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 "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 unsigned int AST_MAX_DEPTH = 50U; TokenList::TokenList(const Settings* settings) : _front(nullptr), _back(nullptr), _settings(settings), _isC(false), _isCPP(false) { } TokenList::~TokenList() { deallocateTokens(); } //--------------------------------------------------------------------------- const std::string& TokenList::getSourceFilePath() const { if (getFiles().empty()) { return emptyString; } return getFiles()[0]; } //--------------------------------------------------------------------------- // Deallocate lists.. void TokenList::deallocateTokens() { deleteTokens(_front); _front = nullptr; _back = nullptr; _files.clear(); } unsigned int TokenList::appendFileIfNew(const std::string &fileName) { // Has this file been tokenized already? for (std::size_t i = 0; i < _files.size(); ++i) if (Path::sameFileName(_files[i], fileName)) return (unsigned int)i; // The "_files" vector remembers what files have been tokenized.. _files.push_back(Path::simplifyPath(fileName)); // Update _isC and _isCPP properties if (_files.size() == 1) { // Update only useful if first file added to _files if (!_settings) { _isC = Path::isC(getSourceFilePath()); _isCPP = Path::isCPP(getSourceFilePath()); } else { _isC = _settings->enforcedLang == Settings::C || (_settings->enforcedLang == Settings::None && Path::isC(getSourceFilePath())); _isCPP = _settings->enforcedLang == Settings::CPP || (_settings->enforcedLang == Settings::None && Path::isCPP(getSourceFilePath())); } } return _files.size() - 1U; } 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 unsigned int lineno, const unsigned 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 + _settings->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 (_back) { _back->insertToken(str); } else { _front = new Token(&_back); _back = _front; _back->str(str); } if (isCPP() && str == "delete") _back->isKeyword(true); _back->linenr(lineno); _back->fileIndex(fileno); } void TokenList::addtoken(const Token * tok, const unsigned int lineno, const unsigned int fileno) { if (tok == nullptr) return; if (_back) { _back->insertToken(tok->str(), tok->originalName()); } else { _front = new Token(&_back); _back = _front; _back->str(tok->str()); if (!tok->originalName().empty()) _back->originalName(tok->originalName()); } _back->linenr(lineno); _back->fileIndex(fileno); _back->flags(tok->flags()); } //--------------------------------------------------------------------------- // 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; unsigned int linenrs = dest->linenr(); const unsigned 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(linenrs); 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()) linenrs += tok->next()->linenr() - tok->linenr(); } return tok2; } //--------------------------------------------------------------------------- // InsertTokens - Copy and insert tokens //--------------------------------------------------------------------------- void TokenList::insertTokens(Token *dest, const Token *src, unsigned 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->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, _files, file0, &outputList); createTokens(&tokens); return outputList.empty(); } //--------------------------------------------------------------------------- void TokenList::createTokens(const simplecpp::TokenList *tokenList) { if (tokenList->cfront()) _files = tokenList->cfront()->location.files; else _files.clear(); _isC = _isCPP = false; if (!_files.empty()) { _isC = Path::isC(getSourceFilePath()); _isCPP = Path::isCPP(getSourceFilePath()); } if (_settings && _settings->enforcedLang != Settings::None) { _isC = (_settings->enforcedLang == Settings::C); _isCPP = (_settings->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 && _settings && str.size() == (2 + _settings->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 (_back) { _back->insertToken(str); } else { _front = new Token(&_back); _back = _front; _back->str(str); } if (isCPP() && _back->str() == "delete") _back->isKeyword(true); _back->fileIndex(tok->location.fileIndex); _back->linenr(tok->location.line); _back->col(tok->location.col); _back->isExpandedMacro(!tok->macro.empty()); } if (_settings && _settings->relativePaths) { for (std::size_t i = 0; i < _files.size(); i++) _files[i] = Path::getRelativePath(_files[i], _settings->basePaths); } Token::assignProgressValues(_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 (std::size_t i = 0; i < tok->str().size(); i++) subchecksum2 += (unsigned int)tok->str()[i]; if (!tok->originalName().empty()) { for (std::size_t i = 0; i < tok->originalName().size(); i++) subchecksum2 += (unsigned int) tok->originalName()[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; unsigned int depth; unsigned int inArrayAssignment; bool cpp; unsigned int assign; bool inCase; explicit AST_state(bool cpp_) : depth(0), inArrayAssignment(0), cpp(cpp_), assign(0U), inCase(false) {} }; 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 (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->previous(), "= ( %name% ) {") && tok->next()->varId() == 0) return true; bool type = false; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { while (tok2->link() && Token::Match(tok2, "(|[|<")) tok2 = tok2->link()->next(); if (tok2->str() == ")") { 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; } // 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(const Token * const tok) { const Token *nameToken = tok; while (nameToken && nameToken->str() == "{") { 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% { !![")) endtok = nameToken->linkAt(1); else if (Token::Match(nameToken,"%name% <") && Token::simpleMatch(nameToken->linkAt(1),"> {")) endtok = nameToken->linkAt(1)->linkAt(1); else return false; // 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 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")) { if (tok->str() == "case") state.inCase = true; compileUnaryOp(tok, state, compileExpression); state.op.pop(); if (state.inCase && Token::simpleMatch(tok, ": ;")) 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(); } 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); 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(), "[{,]")) compileUnaryOp(tok, state, compileExpression); else compileBinOp(tok, state, compileExpression); if (Token::simpleMatch(tok, "} ,")) { tok = tok->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 > 0U) { 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; 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() == "." && 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(); while (Token::Match(curlyBracket, "%name%|.|::|&")) curlyBracket = 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; } } Token* 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 (state.cpp && tok->str() == "{" && iscpp11init(tok)) { if (Token::simpleMatch(tok, "{ }")) compileUnaryOp(tok, state, compileExpression); else compileBinOp(tok, state, compileExpression); if (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* tok2 = tok; tok = tok->link()->next(); compilePrecedence3(tok, state); compileUnaryOp(tok2, 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())) { 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()) { 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() == "&&") { 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 > 0U) 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 unsigned int assign = state.assign; state.assign = 0U; compileBinOp(tok, state, compileAssignTernary); state.assign = assign; } else if (tok->str() == ":") { if (state.depth == 1U && state.inCase) break; if (state.assign > 0U) 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 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 (tok->str() == "[") { if (isLambdaCaptureList(tok)) { tok = const_cast(tok->astOperand1()); if (tok->str() == "(") tok = const_cast(tok->astOperand1()); 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()) return const_cast(tok->astTop()); 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(const_cast(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(const_cast(state3.op.top())); semicolon1->astOperand2(semicolon2); tok->next()->astOperand1(tok); tok->next()->astOperand2(semicolon1); createAstAtTokenInner(endPar->link(), endPar, cpp); return endPar; } if (Token::simpleMatch(tok, "( {")) return tok; if (Token::Match(tok, "%type% <") && !Token::Match(tok->linkAt(1), "> [({]")) return tok->linkAt(1); if (Token::Match(tok, "return|case") || !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); compileExpression(tok, state); Token * const endToken = tok; if (endToken == tok1 || !endToken) return tok1; createAstAtTokenInner(tok1->next(), endToken, cpp); return endToken->previous(); } return tok; } void TokenList::createAst() { for (Token *tok = _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 = _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 safeAstTokens.insert(tok); } } const std::string& TokenList::file(const Token *tok) const { return _files.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 = _front; t; t = t->next()) { if (tok==t) return true; } return false; } void TokenList::simplifyStdType() { for (Token *tok = front(); tok; tok = tok->next()) { if (Token::Match(tok, "char|short|int|long|unsigned|signed|double|float") || (_settings->standards.c >= Standards::C99 && Token::Match(tok, "complex|_Complex"))) { bool isFloat= false; bool isSigned = false; bool isUnsigned = false; bool isComplex = false; unsigned 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 (_settings->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.82/lib/tokenlist.h000066400000000000000000000130031322667425100161640ustar00rootroot00000000000000/* * 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 tokenlistH #define tokenlistH //--------------------------------------------------------------------------- #include "config.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) { _settings = settings; } const Settings *getSettings() const { return _settings; } /** @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 _isC; } /** Is the code CPP. Used for bailouts */ bool isCPP() const { return _isCPP; } /** * Delete all tokens in given token list * @param tok token list to delete */ static void deleteTokens(Token *tok); void addtoken(std::string str, const unsigned int lineno, const unsigned int fileno, bool split = false); void addtoken(const Token *tok, const unsigned int lineno, const unsigned int fileno); static void insertTokens(Token *dest, const Token *src, unsigned 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 */ unsigned int appendFileIfNew(const std::string &fileName); /** get first token of list */ const Token *front() const { return _front; } Token *front() { return _front; } /** get last token of list */ const Token *back() const { return _back; } Token *back() { return _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 _files; } /** * 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; /** * 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 */ Token *_front, *_back; /** filenames for the tokenized source code (source + included) */ std::vector _files; /** settings */ const Settings* _settings; /** File is known to be C/C++ code */ bool _isC, _isCPP; }; /// @} //--------------------------------------------------------------------------- #endif // tokenlistH cppcheck-1.82/lib/utils.h000066400000000000000000000053141322667425100153160ustar00rootroot00000000000000/* * 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 utilsH #define utilsH //--------------------------------------------------------------------------- #include #include #include #include /*! Helper class to aid in the initializing global const data */ template < typename Cont > class make_container { public: typedef make_container< Cont > my_type; typedef typename Cont::value_type T; my_type& operator<< (const T& val) { data_.insert(data_.end(), val); return *this; } my_type& operator<< (const Cont& other_container) { for (typename Cont::const_iterator it=other_container.begin(); it!=other_container.end(); ++it) { data_.insert(data_.end(), *it); } return *this; } my_type& operator<< (T&& val) { data_.insert(data_.end(), val); return *this; } my_type& operator<< (const char* val) { data_.insert(data_.end(), val); return *this; } operator Cont() const { return data_; } private: Cont data_; }; inline bool endsWith(const std::string &str, char c) { return str.back() == c; } inline bool endsWith(const std::string &str, const char end[], std::size_t endlen) { return (str.size() >= endlen) && (str.compare(str.size()-endlen, endlen, end)==0); } 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) #endif cppcheck-1.82/lib/valueflow.cpp000066400000000000000000004371371322667425100165310ustar00rootroot00000000000000#line 2 /* * 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 "valueflow.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "platform.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 namespace { struct ProgramMemory { std::map values; void setValue(unsigned int varid, const ValueFlow::Value &value) { values[varid] = value; } bool getIntValue(unsigned 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 setIntValue(unsigned int varid, MathLib::bigint value) { values[varid] = ValueFlow::Value(value); } bool getTokValue(unsigned 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 hasValue(unsigned int varid) { return values.find(varid) != values.end(); } void swap(ProgramMemory &pm) { values.swap(pm.values); } void clear() { values.clear(); } bool empty() const { return values.empty(); } }; } static void execute(const Token *expr, ProgramMemory * const programMemory, MathLib::bigint *result, bool *error); 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; callstack.push_back(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) { std::list::iterator it; for (it = values.begin(); it != values.end(); ++it) it->changeKnownToPossible(); } /** * Is condition always false when variable has given value? * \param condition top ast token in condition * \param programMemory program memory */ static 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; } /** * Is condition always true when variable has given value? * \param condition top ast token in condition * \param programMemory program memory */ static 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; } /** * Get program memory by looking backwards from given token. */ static ProgramMemory getProgramMemory(const Token *tok, unsigned int varid, const ValueFlow::Value &value) { ProgramMemory programMemory; programMemory.setValue(varid, value); if (value.varId) programMemory.setIntValue(value.varId, value.varvalue); const ProgramMemory programMemory1(programMemory); int indentlevel = 0; for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2, "[;{}] %varid% = %var% ;", varid)) { const Token *vartok = tok2->tokAt(3); programMemory.setValue(vartok->varId(), value); } else if (Token::Match(tok2, "[;{}] %var% =") || Token::Match(tok2, "[;{}] const| %type% %var% (")) { const Token *vartok = tok2->next(); while (vartok->next()->isName()) vartok = vartok->next(); if (!programMemory.hasValue(vartok->varId())) { MathLib::bigint result = 0; bool error = false; execute(vartok->next()->astOperand2(), &programMemory, &result, &error); if (!error) programMemory.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(), programMemory1)) tok2 = cond->previous(); else if (cond && conditionIsTrue(cond->astOperand2(), programMemory1)) { ++indentlevel; continue; } else break; } } return programMemory; } /** * 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; // Is variable protected in LHS.. std::stack tokens; tokens.push(tok->astOperand1()); while (!tokens.empty()) { const Token * const tok2 = tokens.top(); tokens.pop(); if (!tok2 || tok2->str() == ".") continue; // A variable is seen.. if (tok2 != valuetok && tok2->variable() && (tok2->varId() == valuetok->varId() || !tok2->variable()->isArgument())) { // TODO: limit this bailout return tok; } tokens.push(tok2->astOperand2()); tokens.push(tok2->astOperand1()); } } return nullptr; } 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, unsigned 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; } /** 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; // Don't set parent for uninitialized values if (value.isUninitValue()) return; Token *parent = const_cast(tok->astParent()); if (!parent) return; if (parent->str() == "(" && !parent->astOperand2() && Token::Match(parent,"( %name%")) { const ValueType &valueType = ValueType::parseDecl(parent->next(), 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); } } } 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()->values().size() == 1U && parent->astOperand1()->values().front().isKnown()) { 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? std::stack tokens; tokens.push(parent->astOperand1()); unsigned int varId = 0; while (!tokens.empty()) { const Token *t = tokens.top(); tokens.pop(); if (!t) continue; tokens.push(t->astOperand1()); tokens.push(t->astOperand2()); if (t->varId()) { if (varId > 0 || value.varId != 0U) return; varId = t->varId(); } else if (t->str() == "(" && Token::Match(t->previous(), "%name%")) return; // function call } 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 known = ((parent->astOperand1()->values().size() == 1U && parent->astOperand1()->values().front().isKnown()) || (parent->astOperand2()->values().size() == 1U && parent->astOperand2()->values().front().isKnown())); // known result when a operand is 0. if (Token::Match(parent, "[&*]") && value.isKnown() && value.isIntValue() && value.intvalue==0) { setTokenValue(parent, value, settings); return; } for (std::list::const_iterator value1 = parent->astOperand1()->values().begin(); value1 != parent->astOperand1()->values().end(); ++value1) { if (!value1->isIntValue() && !value1->isFloatValue() && !value1->isTokValue()) continue; if (value1->isTokValue() && (!parent->isComparisonOp() || value1->tokvalue->tokType() != Token::eString)) continue; for (std::list::const_iterator value2 = parent->astOperand2()->values().begin(); value2 != parent->astOperand2()->values().end(); ++value2) { if (!value2->isIntValue() && !value2->isFloatValue() && !value2->isTokValue()) continue; if (value2->isTokValue() && (!parent->isComparisonOp() || value2->tokvalue->tokType() != Token::eString || value1->isTokValue())) continue; if (known || value1->varId == 0U || value2->varId == 0U || (value1->varId == value2->varId && value1->varvalue == value2->varvalue && value1->isIntValue() && value2->isIntValue())) { 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->varvalue : value2->varvalue; result.errorPath = (value1->errorPath.empty() ? value2 : value1)->errorPath; if (value1->valueKind == value2->valueKind) result.valueKind = value1->valueKind; 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() == "!") { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (!it->isIntValue()) continue; ValueFlow::Value v(*it); v.intvalue = !v.intvalue; setTokenValue(parent, v, settings); } } // ~ else if (parent->str() == "~") { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (!it->isIntValue()) continue; ValueFlow::Value v(*it); v.intvalue = ~v.intvalue; unsigned 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)<str() == "-" && !parent->astOperand2()) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (!it->isIntValue() && !it->isFloatValue()) continue; ValueFlow::Value v(*it); if (v.isIntValue()) v.intvalue = -v.intvalue; else v.floatValue = -v.floatValue; setTokenValue(parent, v, settings); } } // Array element else if (parent->str() == "[" && parent->astOperand1() && parent->astOperand2()) { for (std::list::const_iterator value1 = parent->astOperand1()->values().begin(); value1 != parent->astOperand1()->values().end(); ++value1) { if (!value1->isTokValue()) continue; for (std::list::const_iterator value2 = parent->astOperand2()->values().begin(); value2 != parent->astOperand2()->values().end(); ++value2) { 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); } } } } } } } static unsigned 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 return 0; } // Handle various constants.. static Token * valueFlowSetConstantValue(const 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(const_cast(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(const_cast(tok), value, settings); } else if (tok->enumerator() && tok->enumerator()->value_known) { ValueFlow::Value value(tok->enumerator()->value); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(const_cast(tok), value, settings); } else if (tok->str() == "NULL" || (cpp && tok->str() == "nullptr")) { ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(const_cast(tok), value, settings); } else if (Token::simpleMatch(tok, "sizeof (")) { const Token *tok2 = tok->tokAt(2); 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(const_cast(tok), value, settings); setTokenValue(const_cast(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(const_cast(tok), value, settings); setTokenValue(const_cast(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 unsigned 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(const_cast(tok->tokAt(4)), value, settings); } } else if (!tok2->type()) { const ValueType &vt = ValueType::parseDecl(tok2,settings); if (vt.pointer) { ValueFlow::Value value(settings->sizeof_pointer); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::CHAR) { ValueFlow::Value value(1); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::SHORT) { ValueFlow::Value value(settings->sizeof_short); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::INT) { ValueFlow::Value value(settings->sizeof_int); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::LONG) { ValueFlow::Value value(settings->sizeof_long); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::LONGLONG) { ValueFlow::Value value(settings->sizeof_long_long); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::FLOAT) { ValueFlow::Value value(settings->sizeof_float); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } else if (vt.type == ValueType::Type::DOUBLE) { ValueFlow::Value value(settings->sizeof_double); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(const_cast(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) { 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 void valueFlowPointerAlias(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { // not address of if (tok->str() != "&" || tok->astOperand2()) 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 valueFlowBitAnd(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->str() != "&") continue; if (tok->values().size() == 1U && tok->values().front().isKnown()) 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 valueFlowOppositeCondition(SymbolDatabase *symboldatabase, const Settings *settings) { for (std::list::iterator scope = symboldatabase->scopeList.begin(); scope != symboldatabase->scopeList.end(); ++scope) { 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; const Token *ifOpenBraceTok = tok2->tokAt(4); const Token *cond2 = ifOpenBraceTok->astOperand2(); if (!cond2 || !cond2->isComparisonOp()) continue; if (isOppositeCond(true, cpp, cond1, cond2, settings->library, true)) { ValueFlow::Value value(1); value.setKnown(); setTokenValue(const_cast(cond2), value, settings); } tok2 = ifOpenBraceTok->link(); } } } 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()->str() == "=") { 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 (tokenList->isCPP() && tok->astParent()->str() == ">>") { const Token *lhs = tok->astParent(); while (Token::simpleMatch(lhs->astParent(), ">>")) lhs = lhs->astParent(); lhs = lhs->astOperand1(); if (!lhs || !lhs->valueType() || !lhs->valueType()->isIntegral()) 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 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 unsigned 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)) { break; } if (tok2->varId() == varid) { // bailout: assignment if (Token::Match(tok2->previous(), "!!* %name% =")) { 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.push_back(ErrorPathItem(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.push_back(ErrorPathItem(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, settings, &inconclusive)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction"); 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; } 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)) { 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 void valueFlowBeforeCondition(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { const std::size_t functions = symboldatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symboldatabase->functionScopes[i]; for (Token* tok = const_cast(scope->classStart); tok != scope->classEnd; 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; } unsigned int varid = vartok->varId(); const Variable * const var = vartok->variable(); if (varid == 0U || !var) 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)) { 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)) { 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; } } valueFlowReverse(tokenlist, tok, 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();) { bool found = false; for (std::list::const_iterator it2 = valuesToRemove.begin(); it2 != valuesToRemove.end(); ++it2) { if (it->intvalue == it2->intvalue) { found = true; break; } } if (found) values.erase(it++); else ++it; } } static void valueFlowAST(Token *tok, unsigned int varid, const ValueFlow::Value &value, const Settings *settings) { if (!tok) return; if (tok->varId() == varid) setTokenValue(tok, value, settings); valueFlowAST(const_cast(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(); bool nonzero = false; for (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { if (it->intvalue != 0) { nonzero = true; break; } } if (!nonzero) return; ProgramMemory pm; pm.setValue(varid,value); if (conditionIsTrue(tok->astOperand1(), pm)) return; } valueFlowAST(const_cast(tok->astOperand2()), varid, value, settings); } /** if known variable is changed in loop body, change it to a possible value */ static void handleKnownValuesInLoop(const Token *startToken, const Token *endToken, std::list *values, unsigned int varid, bool globalvar, const Settings *settings) { bool isChanged = false; for (std::list::iterator it = values->begin(); it != values->end(); ++it) { if (it->isKnown()) { if (!isChanged) { if (!isVariableChanged(startToken, endToken, varid, globalvar, settings)) break; isChanged = true; } it->setPossible(); } } } 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; } static bool valueFlowForward(Token * const startToken, const Token * const endToken, const Variable * const var, const unsigned int varid, std::list values, const bool constValue, const bool subFunction, TokenList * const tokenlist, ErrorLogger * const errorLogger, const Settings * const settings) { int indentlevel = 0; unsigned 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 (indentlevel >= 0 && tok2->str() == "{") ++indentlevel; else if (indentlevel >= 0 && tok2->str() == "}") { --indentlevel; if (indentlevel <= 0 && isReturnScope(tok2) && 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() + " valueFlowForward, bailing out since it's unknown if conditional return is executed"); return false; } bool bailoutflag = false; for (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { 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 (bailoutflag) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForward, conditional return is assumed to be executed"); return false; } } else if (indentlevel <= 0 && Token::simpleMatch(tok2->link()->previous(), "else {") && !isReturnScope(tok2->link()->tokAt(-2)) && isVariableChanged(tok2->link(), tok2, varid, var->isGlobal(), settings)) { changeKnownToPossible(values); } } // skip lambda functions // TODO: handle lambda functions if (Token::simpleMatch(tok2, "= [")) { Token *lambdaEndToken = const_cast(findLambdaEndToken(tok2->next())); if (lambdaEndToken) { tok2 = lambdaEndToken; continue; } } if (Token::Match(tok2, "[;{}] %name% :") || tok2->str() == "case") { changeKnownToPossible(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? bool skipelse = false; const Token *condition = tok2->linkAt(-1); condition = condition ? condition->linkAt(-1) : nullptr; condition = condition ? condition->astOperand2() : nullptr; for (std::list::iterator it = values.begin(); it != values.end(); ++it) { if (conditionIsTrue(condition, getProgramMemory(tok2, varid, *it))) { skipelse = true; break; } } 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)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForward, 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? if (isVariableChanged(tok2->next(), tok2->next()->link(), varid, var->isGlobal(), settings)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForward, 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")) handleKnownValuesInLoop(tok2, tok2->linkAt(1)->linkAt(1), &values, varid, var->isGlobal(), settings); // Set values in condition for (Token* tok3 = tok2->tokAt(2); tok3 != tok2->next()->link(); tok3 = tok3->next()) { if (tok3->varId() == varid) { for (std::list::const_iterator it = values.begin(); it != values.end(); ++it) setTokenValue(tok3, *it, 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 (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { if (condAlwaysTrue) { truevalues.push_back(*it); continue; } if (condAlwaysFalse) { falsevalues.push_back(*it); continue; } const ProgramMemory &programMemory = getProgramMemory(tok2, varid, *it); if (subFunction && conditionIsTrue(condTok, programMemory)) truevalues.push_back(*it); else if (!subFunction && !conditionIsFalse(condTok, programMemory)) truevalues.push_back(*it); if (condAlwaysFalse) falsevalues.push_back(*it); else if (conditionIsFalse(condTok, programMemory)) falsevalues.push_back(*it); else if (!subFunction && !conditionIsTrue(condTok, programMemory)) falsevalues.push_back(*it); } if (truevalues.size() != values.size() || condAlwaysTrue) { // '{' Token * const startToken1 = tok2->linkAt(1)->next(); valueFlowForward(startToken1->next(), startToken1->link(), var, varid, truevalues, constValue, subFunction, tokenlist, errorLogger, settings); if (!condAlwaysFalse && isVariableChanged(startToken1, startToken1->link(), varid, var->isGlobal(), settings)) { removeValues(values, truevalues); changeKnownToPossible(values); } // goto '}' tok2 = startToken1->link(); if (isReturnScope(tok2)) { if (condAlwaysTrue) return false; removeValues(values, truevalues); } if (Token::simpleMatch(tok2, "} else {")) { Token * const startTokenElse = tok2->tokAt(2); valueFlowForward(startTokenElse->next(), startTokenElse->link(), var, varid, falsevalues, constValue, subFunction, tokenlist, errorLogger, settings); if (!condAlwaysTrue && isVariableChanged(startTokenElse, startTokenElse->link(), varid, var->isGlobal(), settings)) { removeValues(values, falsevalues); changeKnownToPossible(values); } // goto '}' tok2 = startTokenElse->link(); if (isReturnScope(tok2)) { if (condAlwaysFalse) return false; removeValues(values, falsevalues); } } continue; } Token * const start = tok2->linkAt(1)->next(); Token * const end = start->link(); bool varusage = (indentlevel >= 0 && constValue && number_of_if == 0U) ? isVariableChanged(start,end,varid,var->isGlobal(),settings) : (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) { std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) setTokenValue(condtok, *it, 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; } // Remove conditional values std::list::iterator it; for (it = values.begin(); it != values.end();) { if (it->condition || it->conditional) values.erase(it++); else { it->changeKnownToPossible(); ++it; } } } // stop after conditional return scopes that are executed if (isReturnScope(end)) { 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)) && (Token::findmatch(start, "return|continue|break|throw", end) || (Token::simpleMatch(end,"} else {") && Token::findmatch(end, "return|continue|break|throw", end->linkAt(2))))) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + ". noreturn conditional scope."); return false; } if (isVariableChanged(start, end, varid, var->isGlobal(), settings)) { if ((!read || number_of_if == 0) && Token::simpleMatch(tok2, "if (") && !(Token::simpleMatch(end, "} else {") && (Token::findmatch(end, "%varid%", end->linkAt(2), varid) || Token::findmatch(end, "return|continue|break|throw", end->linkAt(2))))) { ++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()->classEnd && Token::Match(tok3->scope()->classEnd->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; } } } else if (tok2->str() == "}" && indentlevel == varusagelevel) { ++number_of_if; // Set "conditional" flag for all 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->classEnd); if (tok2 == endToken) break; --indentlevel; changeKnownToPossible(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")) 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() == "?") { const Token *condition = tok2->astOperand1(); const Token *op2 = tok2->astOperand2(); if (!condition || !op2) // Ticket #6713 continue; if (condition->hasKnownIntValue()) { const ValueFlow::Value &condValue = condition->values().front(); const Token *expr = (condValue.intvalue != 0) ? op2->astOperand1() : op2->astOperand2(); std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) valueFlowAST(const_cast(expr), varid, *it, settings); if (isVariableChangedByFunctionCall(expr, varid, settings, nullptr)) changeKnownToPossible(values); } else { std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { const ProgramMemory programMemory(getProgramMemory(tok2, varid, *it)); if (conditionIsTrue(condition, programMemory)) valueFlowAST(const_cast(op2->astOperand1()), varid, *it, settings); else if (conditionIsFalse(condition, programMemory)) valueFlowAST(const_cast(op2->astOperand2()), varid, *it, settings); else valueFlowAST(const_cast(op2), varid, *it, settings); } if (isVariableChangedByFunctionCall(op2, varid, settings, nullptr)) changeKnownToPossible(values); } // Skip conditional expressions.. while (tok2->astOperand1() || tok2->astOperand2()) { if (tok2->astOperand2()) tok2 = const_cast(tok2->astOperand2()); else if (tok2->isUnaryPreOp()) tok2 = const_cast(tok2->astOperand1()); else break; } tok2 = tok2->next(); } 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.push_back(ErrorPathItem(tok2, info)); ++it; } } if (values.empty()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "coumpound assignment of " + tok2->str()); return false; } } // bailout: assignment else if (Token::Match(tok2->previous(), "!!* %name% %assign%")) { // simplify rhs std::stack rhs; rhs.push(const_cast(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) { std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) setTokenValue(rtok, *it, settings); } rhs.push(const_cast(rtok->astOperand1())); rhs.push(const_cast(rtok->astOperand2())); } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str()); return false; } // bailout: possible assignment using >> if (Token::Match(tok2->previous(), ">> %name% >>|;")) { const Token *parent = tok2->previous(); do { parent = parent->astParent(); } while (Token::simpleMatch(parent, ">>")); if (!parent) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Possible assignment of " + tok2->str() + " using >>"); 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 = const_cast(astTop->link()); 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); std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { if (!conditional || !it->conditional) setTokenValue(tok2, *it, 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.push_back(ErrorPathItem(tok2, info)); } } // bailout if address of var is taken.. if (tok2->astParent() && tok2->astParent()->str() == "&" && !tok2->astParent()->astOperand2()) { 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; } // assigned by subfunction? bool inconclusive = false; if (isVariableChangedByFunctionCall(tok2, settings, &inconclusive)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction"); return false; } if (inconclusive) { std::list::iterator it; for (it = values.begin(); it != values.end(); ++it) it->setInconclusive(); } if (tok2->strAt(1) == "." && tok2->next()->originalName() != "->") { if (settings->inconclusive) { std::list::iterator it; for (it = values.begin(); it != values.end(); ++it) it->setInconclusive(); } else { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by member function"); 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)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "valueFlowForward, " + var->name() + " is changed in lambda function"); return false; } } } return true; } static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr) { if (tok->str() != "std") return false; ValueFlow::Value::MoveKind kind = ValueFlow::Value::NonMovedVariable; Token * variableToken = nullptr; if (Token::Match(tok, "std :: move ( %var% )")) { variableToken = tok->tokAt(4); kind = ValueFlow::Value::MovedVariable; } else if (Token::simpleMatch(tok, "std :: forward <")) { Token * leftAngle = tok->tokAt(3); Token * rightAngle = leftAngle->link(); if (Token::Match(rightAngle, "> ( %var% )")) { variableToken = rightAngle->tokAt(2); kind = ValueFlow::Value::ForwardedVariable; } } if (!variableToken) return false; if (variableToken->strAt(2) == ".") // Only partially moved return false; if (moveKind != nullptr) *moveKind = kind; if (varTok != nullptr) *varTok = variableToken; return true; } static bool isOpenParenthesisMemberFunctionCallOfVarId(const Token * openParenthesisToken, unsigned int varId) { const Token * varTok = openParenthesisToken->tokAt(-3); return Token::Match(varTok, "%varid% . %name% (", varId) && varTok->next()->originalName() == emptyString; } static const Token * nextAfterAstRightmostLeaf(Token const * 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()); return rightmostLeaf->next(); } 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; const std::size_t functions = symboldatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symboldatabase->functionScopes[i]; if (!scope) continue; const Token * start = scope->classStart; if (scope->function) { const Token * memberInitializationTok = scope->function->constructorMemberInitialization(); if (memberInitializationTok) start = memberInitializationTok; } for (Token* tok = const_cast(start); tok != scope->classEnd; 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::MOVED; value.moveKind = ValueFlow::Value::NonMovedVariable; value.errorPath.push_back(ErrorPathItem(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 unsigned int varId = varTok->varId(); const Token * const endOfVarScope = var->typeStartToken()->scope()->classEnd; setTokenValue(varTok, value, settings); valueFlowForward(varTok->next(), endOfVarScope, var, varId, values, false, false, tokenlist, errorLogger, settings); continue; } ValueFlow::Value::MoveKind moveKind; if (!isStdMoveOrStdForwarded(tok, &moveKind, &varTok)) continue; const unsigned 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()->varId() == varId) continue; const Variable *var = varTok->variable(); if (!var) continue; const Token * const endOfVarScope = var->typeStartToken()->scope()->classEnd; ValueFlow::Value value; value.valueType = ValueFlow::Value::MOVED; value.moveKind = moveKind; if (moveKind == ValueFlow::Value::MovedVariable) value.errorPath.push_back(ErrorPathItem(tok, "Calling std::move(" + varTok->str() + ")")); else // if (moveKind == ValueFlow::Value::ForwardedVariable) value.errorPath.push_back(ErrorPathItem(tok, "Calling std::forward(" + varTok->str() + ")")); value.setKnown(); std::list values; values.push_back(value); const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok); const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove); if (endOfFunctionCall) valueFlowForward(const_cast(endOfFunctionCall), endOfVarScope, var, varId, values, false, false, tokenlist, errorLogger, settings); } } } static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { const std::size_t functions = symboldatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { std::set aliased; const Scope * scope = symboldatabase->functionScopes[i]; for (Token* tok = const_cast(scope->classStart); tok != scope->classEnd; tok = tok->next()) { // Alias if (tok->str() == "&" && !tok->astOperand2() && tok->astOperand1()) { aliased.insert(tok->astOperand1()->varId()); continue; } // Assignment if ((tok->str() != "=") || (tok->astParent())) continue; // Lhs should be a variable if (!tok->astOperand1() || !tok->astOperand1()->varId()) continue; const unsigned 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; const Token * const endOfVarScope = var->typeStartToken()->scope()->classEnd; // Rhs values.. if (!tok->astOperand2() || tok->astOperand2()->values().empty()) continue; std::list values = tok->astOperand2()->values(); for (std::list::iterator it = values.begin(); it != values.end(); ++it) { const std::string info = "Assignment '" + tok->expressionString() + "', assigned value is " + it->infoString(); it->errorPath.push_back(ErrorPathItem(tok->astOperand2(), info)); } const bool constValue = tok->astOperand2()->isNumber(); 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 != 0); } } // Static variable initialisation? if (var->isStatic() && var->nameToken() == tok->astOperand1()) changeKnownToPossible(values); // Skip RHS const Token * nextExpression = nextAfterAstRightmostLeaf(tok); valueFlowForward(const_cast(nextExpression), endOfVarScope, var, varid, values, constValue, false, tokenlist, errorLogger, settings); } } } static void valueFlowAfterCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { const std::size_t functions = symboldatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symboldatabase->functionScopes[i]; std::set aliased; for (Token* tok = const_cast(scope->classStart); tok != scope->classEnd; tok = tok->next()) { const Token *vartok, *numtok; if (Token::Match(tok, "= & %var% ;")) aliased.insert(tok->tokAt(2)->varId()); // Comparison if (Token::Match(tok, "==|!=|>=|<=")) { if (!tok->astOperand1() || !tok->astOperand2()) continue; if (tok->astOperand1()->hasKnownIntValue()) { numtok = tok->astOperand1(); vartok = tok->astOperand2(); } else { numtok = tok->astOperand2(); vartok = tok->astOperand1(); } if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) vartok = vartok->astOperand1(); if (!vartok->isName() || !numtok->hasKnownIntValue()) continue; } else if (tok->str() == "!") { vartok = tok->astOperand1(); numtok = nullptr; if (!vartok || !vartok->isName()) continue; } else if (tok->isName() && (Token::Match(tok->astParent(), "%oror%|&&") || Token::Match(tok->tokAt(-2), "if|while ( %var% [)=]"))) { vartok = tok; numtok = nullptr; } else { continue; } const unsigned int varid = vartok->varId(); if (varid == 0U) continue; const Variable *var = vartok->variable(); if (!var || !(var->isLocal() || var->isGlobal() || var->isArgument())) continue; if (aliased.find(varid) != aliased.end()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, vartok, "variable is aliased so we just skip all valueflow after condition"); continue; } std::list values; values.push_back(ValueFlow::Value(tok, numtok ? numtok->values().front().intvalue : 0LL)); if (Token::Match(tok->astParent(), "%oror%|&&")) { Token *parent = const_cast(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 = const_cast(parent->astParent())) { std::stack tokens; tokens.push(const_cast(parent->astOperand2())); bool assign = false; while (!tokens.empty()) { Token *rhstok = tokens.top(); tokens.pop(); if (!rhstok) continue; tokens.push(const_cast(rhstok->astOperand1())); tokens.push(const_cast(rhstok->astOperand2())); if (rhstok->varId() == varid) setTokenValue(rhstok, values.front(), settings); else if (Token::Match(rhstok, "++|--|=") && Token::Match(rhstok->astOperand1(), "%varid%", varid)) { assign = true; break; } } if (assign) break; while (parent->astParent() && parent == parent->astParent()->astOperand2()) parent = const_cast(parent->astParent()); } } } const Token *top = tok->astTop(); if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) { // does condition reassign variable? if (tok != top->astOperand2() && Token::Match(top->astOperand2(), "%oror%|&&") && isVariableChanged(top, top->link(), varid, var->isGlobal(), settings)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "assignment in condition"); continue; } // start token of conditional code Token *startToken = nullptr; // based on the comparison, should we check the if or while? int codeblock = 0; if (Token::Match(tok, "==|>=|<=|!")) codeblock = 1; else if (Token::Match(tok, "%name%|!=")) codeblock = 2; // determine startToken based on codeblock if (codeblock > 0) { // if astParent is "!" we need to invert codeblock const Token *parent = tok->astParent(); while (parent && parent->str() == "&&") parent = parent->astParent(); if (parent && parent->str() == "!") codeblock = (codeblock == 1) ? 2 : 1; // convert codeblock to a startToken if (codeblock == 1 && Token::simpleMatch(top->link(), ") {")) startToken = top->link()->next(); else if (Token::simpleMatch(top->link()->linkAt(1), "} else {")) startToken = top->link()->linkAt(1)->tokAt(2); } if (startToken) { if (values.size() == 1U && Token::Match(tok, "==|!")) { const Token *parent = tok->astParent(); while (parent && parent->str() == "&&") parent = parent->astParent(); if (parent && parent->str() == "(") values.front().setKnown(); } if (!valueFlowForward(startToken->next(), startToken->link(), var, varid, values, true, false, tokenlist, errorLogger, settings)) continue; values.front().setPossible(); if (isVariableChanged(startToken, startToken->link(), varid, var->isGlobal(), settings)) { // TODO: The endToken should not be startToken->link() in the valueFlowForward call if (settings->debugwarnings) bailout(tokenlist, errorLogger, startToken->link(), "valueFlowAfterCondition: " + var->name() + " 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 isreturn = (codeblock == 1 && isReturnScope(after)); 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; } isreturn |= (codeblock == 2 && isReturnScope(after)); } if (!isreturn) { // 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; valueFlowForward(after->next(), top->scope()->classEnd, var, varid, values, constValue, false, tokenlist, errorLogger, settings); } } } } } } static 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->str() == "=") { execute(expr->astOperand2(), programMemory, result, error); if (!*error && expr->astOperand1() && expr->astOperand1()->varId()) programMemory->setIntValue(expr->astOperand1()->varId(), *result); else *error = true; } 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)) { if (expr->astOperand1()->values().size() != 1U || !expr->astOperand1()->values().front().isTokValue()) { *error = true; return; } tokvalue = expr->astOperand1()->values().front().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; } static bool valueFlowForLoop1(const Token *tok, unsigned 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 std::stack tokens; tokens.push(secondExpression); while (!tokens.empty()) { const Token *t = tokens.top(); tokens.pop(); if (!t) continue; if (t->str() == "=" && t->astOperand1() && programMemory.hasValue(t->astOperand1()->varId())) // TODO: investigate what variable is assigned. return false; tokens.push(t->astOperand1()); tokens.push(t->astOperand2()); } } ProgramMemory startMemory(programMemory); ProgramMemory endMemory; unsigned int maxcount = 10000; while (result != 0 && !error && --maxcount) { 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 unsigned 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)) 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, unsigned 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()->classEnd; else endToken = fortok->scope()->classEnd; std::list values; values.push_back(ValueFlow::Value(num)); values.back().errorPath.push_back(ErrorPathItem(fortok,"After for loop, " + var->name() + " has value " + values.back().infoString())); valueFlowForward(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 (std::list::const_iterator scope = symboldatabase->scopeList.begin(); scope != symboldatabase->scopeList.end(); ++scope) { if (scope->type != Scope::eFor) continue; Token* tok = const_cast(scope->classDef); Token* const bodyStart = const_cast(scope->classStart); if (!Token::simpleMatch(tok->next()->astOperand2(), ";") || !Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) continue; unsigned 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 unsigned int varid2 = arg->declarationId(); if (!varid2) return; valueFlowForward(const_cast(functionScope->classStart->next()), functionScope->classEnd, arg, varid2, argvalues, false, true, tokenlist, errorLogger, settings); } static void valueFlowSwitchVariable(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (std::list::iterator scope = symboldatabase->scopeList.begin(); scope != symboldatabase->scopeList.end(); ++scope) { 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->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); continue; } if (Token::Match(tok, "case %num% :")) { std::list values; values.push_back(ValueFlow::Value(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.push_back(ErrorPathItem(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.push_back(ValueFlow::Value(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.push_back(ErrorPathItem(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(); valueFlowForward(tok->tokAt(3), vartok->variable()->scope()->classEnd, 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 (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { const ValueFlow::Value &value = *it; if (value.isIntValue()) setTokenValue(tok, value, settings); } } static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings) { std::istringstream istr(returnValue); TokenList tokenList(settings); if (!tokenList.createTokens(istr)) return; const Token *arg1 = tok->astOperand2(); while (arg1 && arg1->str() == ",") arg1 = arg1->astOperand1(); if (Token::findsimplematch(tokenList.front(), "arg1") && !arg1) return; if (Token::simpleMatch(tokenList.front(), "strlen ( arg1 )") && arg1) { for (std::list::const_iterator it = arg1->values().begin(); it != arg1->values().end(); ++it) { const ValueFlow::Value &value = *it; if (value.isTokValue() && value.tokvalue->tokType() == Token::eString) { ValueFlow::Value retval(value); // copy all "inconclusive", "condition", etc attributes // set return value.. retval.valueType = ValueFlow::Value::INT; retval.tokvalue = nullptr; retval.intvalue = Token::getStrLength(value.tokvalue); setTokenValue(tok, retval, settings); } } return; } // combine operators, set links, etc.. for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[!<>=] =")) { tok2->str(tok2->str() + "="); tok2->deleteNext(); } } // Evaluate expression tokenList.createAst(); valueFlowNumber(&tokenList); for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (tok2->str() == "arg1" && arg1) { setTokenValues(tok2, arg1->values(), settings); } } // Find result.. for (const Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (!tok2->astParent() && !tok2->values().empty()) { setTokenValues(tok, tok2->values(), settings); return; } } } 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; const std::vector &callArguments = getArguments(tok); for (unsigned 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; if (Token::Match(argtok, "%comp%|%oror%|&&|!") && !argtok->hasKnownIntValue()) { argvalues.push_back(ValueFlow::Value(0)); argvalues.push_back(ValueFlow::Value(1)); } else { argvalues = argtok->values(); } if (argvalues.empty()) continue; // Error path.. for (std::list::iterator it = argvalues.begin(); it != argvalues.end(); ++it) { std::string nr = MathLib::toString(argnr + 1) + getOrdinalText(argnr + 1); it->errorPath.push_back(ErrorPathItem(argtok, "Calling function '" + calledFunction->name() + "', " + nr + " argument '" + argvar->name() + "' value is " + it->infoString())); } // passed values are not "known".. changeKnownToPossible(argvalues); valueFlowInjectParameter(tokenlist, errorLogger, settings, argvar, calledFunctionScope, argvalues); } } } static void valueFlowFunctionDefaultParameter(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { if (!tokenlist->isCPP()) return; const std::size_t functions = symboldatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope* scope = symboldatabase->functionScopes[i]; 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 (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { ValueFlow::Value v(*it); 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; // 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->classStart, "{ return")) { if (functionScope && tokenlist->getSettings()->debugwarnings && Token::findsimplematch(functionScope->classStart, "return", functionScope->classEnd)) 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->classStart->next()->astOperand1(), &programMemory, &result, &error); if (!error) { ValueFlow::Value v(result); if (function->isVirtual()) 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::True) || !var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow()) continue; ValueFlow::Value uninitValue; uninitValue.setKnown(); uninitValue.valueType = ValueFlow::Value::UNINIT; std::list values; values.push_back(uninitValue); const bool constValue = true; const bool subFunction = false; valueFlowForward(vardecl->next(), vardecl->scope()->classEnd, var, vardecl->varId(), values, constValue, subFunction, tokenlist, errorLogger, settings); } } ValueFlow::Value::Value(const Token *c, long long val) : valueType(INT), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(NonMovedVariable), varvalue(val), condition(c), varId(0U), conditional(false), defaultArg(false), valueKind(ValueKind::Possible) { errorPath.push_back(ErrorPathItem(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 ""; }; throw InternalError(nullptr, "Invalid ValueFlow Value type"); } const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(const 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->values().size() == 1U && expr->values().front().isKnown() ? &expr->values().front() : nullptr; } 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); valueFlowGlobalStaticVar(tokenlist, settings); valueFlowPointerAlias(tokenlist); valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowBitAnd(tokenlist); valueFlowOppositeCondition(symboldatabase, settings); valueFlowBeforeCondition(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterCondition(tokenlist, symboldatabase, errorLogger, 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); } 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.82/lib/valueflow.h000066400000000000000000000137431322667425100161670ustar00rootroot00000000000000/* * 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 valueflowH #define valueflowH //--------------------------------------------------------------------------- #include "config.h" #include #include #include class ErrorLogger; class Settings; class SymbolDatabase; class Token; class TokenList; namespace ValueFlow { class CPPCHECKLIB Value { public: typedef std::pair ErrorPathItem; typedef std::list ErrorPath; explicit Value(long long val = 0) : valueType(INT), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(NonMovedVariable), varvalue(val), condition(nullptr), varId(0U), conditional(false), defaultArg(false), valueKind(ValueKind::Possible) {} Value(const Token *c, long long val); bool operator==(const Value &rhs) const { if (valueType != rhs.valueType) return false; switch (valueType) { case INT: if (intvalue != rhs.intvalue) return false; break; case TOK: if (tokvalue != rhs.tokvalue) return false; break; case FLOAT: // TODO: Write some better comparison if (floatValue > rhs.floatValue || floatValue < rhs.floatValue) return false; break; case MOVED: if (moveKind != rhs.moveKind) return false; break; case UNINIT: break; }; return varvalue == rhs.varvalue && condition == rhs.condition && varId == rhs.varId && conditional == rhs.conditional && defaultArg == rhs.defaultArg && valueKind == rhs.valueKind; } std::string infoString() const; enum ValueType { INT, TOK, FLOAT, MOVED, UNINIT } valueType; bool isIntValue() const { return valueType == INT; } bool isTokValue() const { return valueType == TOK; } bool isFloatValue() const { return valueType == FLOAT; } bool isMovedValue() const { return valueType == MOVED; } bool isUninitValue() const { return valueType == UNINIT; } /** 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 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 */ unsigned int varId; /** Conditional value */ bool conditional; /** Is this value passed as default parameter to the function? */ bool defaultArg; static const char * toString(MoveKind moveKind) { switch (moveKind) { case NonMovedVariable: return "NonMovedVariable"; case MovedVariable: return "MovedVariable"; case ForwardedVariable: return "ForwardedVariable"; } return ""; } /** How known is this value */ enum ValueKind { /** This value is possible, other unlisted values may also be possible */ Possible, /** Only listed values are possible */ Known, /** Inconclusive */ Inconclusive } 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; } 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(const 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); } #endif // valueflowH cppcheck-1.82/lib/version.h000066400000000000000000000010631322667425100156400ustar00rootroot00000000000000#define CPPCHECK_MAJOR 1 #define CPPCHECK_MINOR 82 #define CPPCHECK_DEVMINOR 82 #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-2017 Cppcheck team." cppcheck-1.82/lib/version.rc000066400000000000000000000016371322667425100160240ustar00rootroot00000000000000#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.82/man/000077500000000000000000000000001322667425100140075ustar00rootroot00000000000000cppcheck-1.82/man/buildman.sh000077500000000000000000000002111322667425100161330ustar00rootroot00000000000000#!/bin/sh xsltproc -o manual.html /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl manual.docbook docbook2pdf manual.docbook cppcheck-1.82/man/cppcheck-design.docbook000066400000000000000000000130351322667425100204020ustar00rootroot00000000000000 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 can not 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.82/man/cppcheck.1.xml000066400000000000000000000600231322667425100164510ustar00rootroot00000000000000 .
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: all Enable all checks. It is recommended to only use --enable=all when the whole program is scanned, because this enables unusedFunction. warning Enable warning messages style Enable all coding style checks. All messages with the severities 'style', 'performance' and 'portability' are enabled. performance Enable performance messages portability Enable portability messages information Enable information messages unusedFunction Check for unused functions. It is recommend to only enable this when the whole program is scanned missingInclude Warn 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. Specifies platform specific types and sizes.The available platforms are: unix32 32 bit unix variant unix64 64 bit unix variant win32A 32 bit Windows ASCII character encoding win32W 32 bit Windows UNICODE character encoding win64 64 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: posix POSIX compatible code c89 C code is C89 compatible c99 C code is C99 compatible c11 C code is C11 compatible (default) c++03 C++ code is C++03 compatible c++11 C++ code is C++11 compatible (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. 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: http://cppcheck.wiki.sourceforge.net/
cppcheck-1.82/man/images/000077500000000000000000000000001322667425100152545ustar00rootroot00000000000000cppcheck-1.82/man/images/gui-newproject-addons.png000066400000000000000000000502311322667425100221730ustar00rootroot00000000000000PNG  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.82/man/images/gui-newproject-pathsanddefines.png000066400000000000000000001071541322667425100240720ustar00rootroot00000000000000PNG  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.82/man/images/gui-newproject.png000066400000000000000000001051601322667425100207270ustar00rootroot00000000000000PNG  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.82/man/images/gui-results.png000066400000000000000000003163261322667425100202600ustar00rootroot00000000000000PNG  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.82/man/manual-ja.docbook000066400000000000000000001734471322667425100172360ustar00rootroot00000000000000 Cppcheck 1.73 dev 2015-09-09 イントロダクション Cppcheck は C/C++の静的解析ツールです。C/C++ コンパイラやその他の解析ツールとは異なり、シンタックスエラーを検出しません。 その代わりに、Cppcheckは、コンパイラが通常、検出に失敗するような種類のバグを検出します。このプロジェクトのゴールは、擬陽性 0 です。 サポートしているプログラムのソースコードとプラットフォーム: さまざまなコンパイラの拡張構文や、インラインアセンブル等を含む、非標準的なソースコードをチェックできます。 Cppcheck は 最新のC++規格をサポートしている、あらゆるC++コンパイラでコンパイルできるようにしています。 Cppcheck は 十分なCPUパワーとメモリーのある、あらゆるプラットフォームで動作するようにしています。 Cppcheckに限界があることをご理解ください。Cppcheckの報告しているエラーに稀に間違いのあることがあります。また、Cppcheck が検出しないバグが残っていることもあります。 ソフトウェアを注意深くテストすれば、Cppcheckを使うより、より多くのバグを検出することができるでしょう。ソフトウェアを注意深く実装すれば、Cppcheckを使うより、より多くのバグを検出することができるでしょう。しかし、あなたのソフトウェアを実装するときやテストするときに見逃したバグのいくつかを Cppcheckが検出できるでしょう。 はじめ方
最初のテスト これは単純なソースコードです。 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に指定することです。 cppcheck src/a src/b src/asrc/b 以下の全てのファイルだけをチェックします。 第二の方法は、-iオプションと共に除外したいファイルやフォルダを指定することです。次のコマンドではsrc/c以下のファイルをチェックしません。 cppcheck -isrc/c src
インクルードパス指定 インクルードパスを追加するには-Iオプションに続けてパスを指定します。 Cppcheckのプリプロセッサは基本的に他のプリプロセッサと同様にインクルードを扱います。しかし、その他のプリプロセッサはヘッダファイルが見つからない場合に停止するのとは違って、cppcheckはただ単に、メッセージ情報を表示してソースコードの解析を続けます。 cppcheckは常にソースコード全体を確認する必要がないので、このような仕様になっています。実際に、全てのインクルードパスを与えないことを推奨しています。もちろん、クラスのメンバーの実装を確認した上でクラスの宣言をCppcheckでチェックするのは有用ではありますが、標準ライブラリのヘッダーをCppcheckに確認させるのは有用ではありません。というのは、チェックにかかる時間が長くなり、あまりよくない結果が表示されるからです。そのような場合、.cfg ファイル (後述します)によってcppcheckに関数や型の実装の情報を提供する方がよいでしょう。
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>
プリプロセッサの設定 Cppcheckはデフォルトでプリプロセッサのデファインのコンパイルスイッチ設定の組み合わせを全てチェックします。(ただし、これらのうち #error を除く) これを変更するには -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が定義されている組み合わせをチェックしません。 XML出力 Cppcheckは出力をXML形式に変更できます。XML出力には古いXML 形式(version 1) と新しいXML 形式(version 2)があります。可能であれば、新しい形式をご利用ください。 古い形式は後方互換性のためだけに残してあります。現在、変更の予定はありませんが、いつかは古い形式をサポートしなくなります。--xml オプションでフォーマットを指定します。 新しい形式は古い形式にあったいくつかの問題を解消しています。新しい形式も、おそらくいつかは、新しい属性や要素を追加するなど変更されるかもしれません。Cppcheckでファイルをチェックし、新しいXML形式で出力するコマンドのサンプルです。: cppcheck --xml-version=2 file1.cpp ここにバージョン2の出力の例を挙げます。: <?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 file="file.c" 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 ファイル名相対パスまたは絶対パスのどちらかです。 line msg この属性は使用しません。ただし将来のいつか、それぞれの位置に短いメッセージを追加するようになるかもしれません。
出力の形式の変更 もし、テンプレートを使用して、出力の形式を変更することができます。 Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。 cppcheck --template=vs gui/test.cpp このオプションは出力形式を次のように変更します。: Checking gui/test.cpp... gui/test.cpp(31): error: Memory leak: b gui/test.cpp(16): error: Mismatching allocation and deallocation: k gcc に互換性のある形式が必要な場合には、--template=gccを使用します。: cppcheck --template=gcc gui/test.cpp このオプションは出力形式を次のように変更します。: Checking gui/test.cpp... gui/test.cpp:31: error: Memory leak: b gui/test.cpp:16: error: Mismatching allocation and deallocation: k それ以外に、自分自身の作成したパターンで指定することもできます。例としてコンマ区切りで出力してみましょう。: cppcheck --template="{file},{line},{severity},{id},{message}" gui/test.cpp このオプションは出力形式を次のように変更します。: Checking gui/test.cpp... gui/test.cpp,31,error,memleak,Memory leak: b gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocation: k 以下のようなフォーマット指定項目がサポートされています。 callstack callstack ただし可能な場合に限る。 file ファイル名 id メッセージid line 行数 message 長い形式のメッセージ severity メッセージの種類、レベル その他エスケープシーケンス \b (バックスペース), \n (改行), \r (改ページ) , \t (タブ) がサポートされています。 出力の抑制 ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。
エラー種による出力の抑制 エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。: [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/
インライン出力抑制 エラー出力の抑制をソースコーオ中に直接、コメントの形で記載することもできます。このコメントには特別なキーワードを含めて記載します。ただし、インライン出力を抑制するコメントをソースコードに追加すると、ソースコードの可読性が少し悪くなってしまうかもしれません。 このソースコードは通常エラメッセージを出力する例です。: 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
ライブラリ設定 windows, posix, gtk, qt,他の外部のライブラリを使用した場合、Cppcheckは外部の関数がどのようなものであるかがわかりません。Cppcheck はそのため、メモリリークやバッファオーバーフロー、ヌルポインタのデリファレンスの可能性といったさまざまな問題が検出できません。これを解決するには設定ファイル(.cfg file)を使用します。 もしあなたが有名なライブラリの設定ファイルを作成したときには、私達のサイトにアップロードしてくれると非常に助かります。
カスタム設定ファイル(.cfg file)の使用 あなたのプロジェクト専用の設定ファイルを作成し、使用することができます。そのためには、--check-library--enable=information を使用して設定のためのヒントを入手します。 コマンドラインのcppcheck はカスタマイズした設定ファイル(.cfg files)を作業パスから読み込もうとします。作業パスはcppcheckを実行しているパスですでそこに設定ファイルがあると考えます。 GUIのcppcheckはプロジェクトのファイルパスから設定ファイルを読み込もうとします。カスタマイズした設定ファイル(.cfg file)は プロジェクトファイルの編集 ダイアログで確認できます。このタイアログを表示させるにはファイル メニューから開いてください。
メモリーリソースのリーク Cppcheck はリークのチェックについての設定できます。
alloc と dealloc ここにサンプルのプログラムがあります。: void test() { HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); } このサンプルには、リーソースのリークがあります。CreatePen()はWindowsの関数でpenを作成します。しかし、Cppcheck はこの関数が返す値を解放しなければならないことがわかりません。そのためエラーメッセージは表示されません。: # cppcheck pen1.c Checking pen1.c... もし、windowsの設定ファイルを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>
leak-ignore とuse しばしば、割り当てられたポインタを関数に渡すことがあります。例: void test() { char *p = malloc(100); dostuff(p); } もし設定ファイルがなく、Cppcheckがdostuffの仕様を把握していなければ、Cppcheckはdostuffがメモリーについて配慮しており、メモリーリークは発生しないと仮定します。 dostuffがメモリーについて配慮せず、解放などを行なっていないことを指定するためには、leak-ignoreを使用します。: <?xml version="1.0"?> <def> <function name="dostuff"> <leak-ignore/> <arg nr="1"/> <arg nr="2"/> </function> </def> これとは逆にdostuffがメモリーについて配慮している場合には次のように設定します。: <?xml version="1.0"?> <def> <memory> <alloc>malloc</alloc> <dealloc>free</dealloc> <use>dostuff</use> </memory> </def> なお、この<use>設定には論理的には全く無意味です。この設定がない場合でも同じエラーが表示されます。これは--check-libraryのinformationメッセージを減らすために使用します。
関数引数: 未初期化メモリ ここにサンプルのプログラムがあります。: void test() { char buffer1[1024]; char buffer2[1024]; CopyMemory(buffer1, buffer2, 1024); } このプログラムのバグは buffer2 が初期化されていないことです。CopyMemory 関数の第二引数は初期化されている必要があります。しかし、Cppcheckは関数に未初期化の変数を渡してもよいと仮定しています。: # cppcheck uninit.c Checking uninit.c... もし、windowsの設定ファイルを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... もし、windowsの設定ファイルをCppcheckに提供すれば、この問題を指摘することができるようになります。: cppcheck --library=windows.cfg null.c Checking null.c... [null.c:3]: (error) Null pointer dereference これが最小限の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. The value is 1024 but the valid values are '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までの値(両端含む)が有効な値です。
関数引数: 最小サイズ いくつかの関数はバッファーを引数にとります。バッファの最小サイズを指定することができます。(要素数ではなくバイト数です。)想像してください。: 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 バッファーのサイズがその他の引数のバッファーのサイズより大きくなればなりません。例: std.cfg のstrcnpyの設定を参照してください mul バッファーのサイズがその他の2つの引数の値の積より大きくなればなりません。典型的な使用例としては、一つの引数が構造体などの要素のサイズを指定し、もうひとつの引数が要素の個数を定義するような場合です。例: std.cfg のfreadの設定を参照してください
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> </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... [noreturn.c:3]: (warning) Return value of function strcmp() is not used. これが最小限のlib.cfg ファイルです。: <?xml version="1.0"?> <def> <function name="strcmp"> <use-retval/> </function> </def>
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>
関数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>が使用されています。
全ての引数に対する指定 引数の数に -1を指定すると、チェック時にその関数の全ての引数に適用されます。それぞれの引数に対する設定は、全ての引数に対する指定を上書きします。
ルール(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.82/man/manual.docbook000066400000000000000000001754631322667425100166460ustar00rootroot00000000000000 Cppcheck 1.82 2017-08-12 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. Instead, Cppcheck 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. 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) Start the GUI.
New Project It is not required but creating a new project file is a good first step. You do so through File and New project file.
New Project - Paths and Defines What kind of project do you have? If it is a Visual Studio project or if you can generate a compile database (cmake/qbs/etc), then you can import the project. Otherwise you can configure the paths and defines manually. In this screenshot below, a Visual Studio project file is imported:
New Project - Project In the Project tab it is highly recommended that a Cppcheck build dir is configured. This will be used by Cppcheck to store various analysis information. It gives you whole program analysis, incremental analysis, statistics, etc. Each project should have its own unique build dir. In the screenshot below the build dir is configured as cppcheck-build-dir. The path is relative to the project file. You should also choose all the libraries that you use. In the screenshot below the microsoft_sal and windows libraries are selected. You can read more about libraries in this manual.
New Project - Addons We skip the Exclude and Suppressions tabs now, they can be used later to tweak the results. In the Addons tab you can add extra analysis. The addons require python.
Analyze Click the OK button in the dialog. Analysis will start immediately. All warnings are activated and therefore it is pretty noisy. There are likely various warnings that you don't care about. You can fix that easily, right click on messages and choose Hide or Suppress. Hiding messages is not permanent, they will be shown after next analysis. Suppressing messages is permanent, suppressed ids are stored in the project file and those will not be shown again.
Getting started (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). Using the project file is quicker since it requires very little configuration from you. Checking files manually gives you better control of the analysis. We don't know which approach 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 does not currently work with the --project option and 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.
Enable messages By default only error messages are shown. Through the --enable command more checks can be enabled. # enable warning messages cppcheck --enable=warning file.c # enable performance messages cppcheck --enable=performance file.c # enable information messages cppcheck --enable=information file.c # For historical reasons, --enable=style enables warning, performance, # portability and style messages. These are all reported as "style" when # using the old xml format. cppcheck --enable=style file.c # enable warning and performance messages cppcheck --enable=warning,performance file.c # enable unusedFunction checking. This is not enabled by --enable=style # because it doesn't work well on libraries. cppcheck --enable=unusedFunction file.c # enable all messages cppcheck --enable=all Please note that --enable=unusedFunction should only be used when the whole program is scanned. Therefore, --enable=all should also only be used when the whole program is scanned. The reason is that the unusedFunction checking will warn if a function is not called. There will be noise if function calls are not seen.
Inconclusive checks By default Cppcheck only writes error messages if it is certain. With --inconclusive error messages will also be written when the analysis is inconclusive. cppcheck --inconclusive path This can of course cause false warnings, it might be reported that there are bugs even though there are not. Only use this command if false warnings are acceptable.
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
Multithreaded checking The option -j is used to specify the number of threads you want to use. For example, to use 4 threads to check the files in a folder: cppcheck -j 4 path Please note that this will disable unusedFunction checking.
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: <?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 When you use CMake or Visual Studio you can use --project to analyse your project. It will give you quick and easy results. There is not much configuration you need to do. But it is hard to say if this will give you the best results, it is recommended that you try it and also try to analyse your source code without --project and see which option works best for you.
CMake Cppcheck can understand compile databases. You can generate these with CMake. Example: $ 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
Visual Studio You can run Cppcheck on individual project files (*.vcxproj) or on a whole solution (*.sln) # run cppcheck on a whole solution $ cppcheck --project=foobar.sln # run cppcheck on a individual project $ cppcheck --project=foobar.vcxproj Please note that there is also a Visual Studio plugin that allows you to run cppcheck inside Visual Studio.
Preprocessor settings If you use --project then Cppcheck will use the preprocessor settings from the project file. Otherwise you'll probably want to configure the include paths, defines etc.
Defines Here is a file that has 2 configurations (with A defined and without A): #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 be analysed both when A is defined and when it is not. 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 undefines a symbol. 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.
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: <?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>
The <error> element Each error is reported in a <error> element. Attributes: id id of error. These are always valid symbolnames. severity either: error, warning, style, performance, portability or information msg the error message in short format verbose the error message in long format. inconclusive This attribute is only used when the message is inconclusive. cwe CWE ID for message. This attribute is only used when the CWE ID for the message is known.
The <location> element All locations related to an error is listed with <location> 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 a number msg this attribute doesn't exist yet. But in the future we may add a short message for each location.
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 The following format specifiers are supported: callstack callstack - if available file filename id message id line line number message verbose message text severity a type/rank of message The escape sequences \b (backspace), \n (newline), \r (formfeed) and \t (horizontal tab) are supported. Suppressions If you want to filter out certain errors you can suppress these.
Suppressing a certain error type 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 --xml command line flag. Copy and paste the id string from the XML output. This may be * to suppress all warnings (for a specified file or files). 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.
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/
Listing 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/
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 Checking 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
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. It is recommended that you use the Library Editor in the Cppcheck GUI to edit configuration files. It is available in the View menu. All settings are not documented in this manual. If you have a question about the .cfg file format it is recommended you ask in the forum (http://sourceforge.net/p/cppcheck/discussion/). The command line cppcheck will try to load custom .cfg files from the working path - execute cppcheck from the path where the .cfg files are. The cppcheck GUI will try to load custom .cfg files from the project file path. The custom .cfg files should be shown in the Edit Project File dialog that you open from the File menu.
Memory/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.
alloc and dealloc 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: <?xml version="1.0"?> <def> <resource> <alloc>CreatePen</alloc> <dealloc>DeleteObject</dealloc> </resource> </def> The allocation and deallocation functions are organized in groups. Each group is defined in a <resource> or <memory> tag and is identified by its <dealloc> functions. This means, groups with overlapping <dealloc> tags are merged.
leak-ignore and use 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 leak-ignore in the <function> tag (see next section): <?xml version="1.0"?> <def> <function name="dostuff"> <leak-ignore/> <arg nr="1"/> </function> </def> If instead dostuff takes care of the memory then this can be configured with: <?xml version="1.0"?> <def> <memory> <dealloc>free</dealloc> <use>dostuff</use> </memory> </def> The <use> configuration has no logical purpose. You will get the same warnings without it. Use it to silence --check-library information messages.
Function behaviour To specify the behaviour of functions and how they should be used, <function> 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: <function name="memcpy,std::memcpy">. If you have template functions then provide their instantiated names <function name="dostuff<int>">.
Function arguments The arguments a function takes can be specified by <arg> 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.
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 <?xml version="1.0"?> <def> <function name="MemCmp"> <arg nr="1"/> <arg nr="2"/> <arg nr="3"> <not-bool/> </arg> </function> </def>
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: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"/> <arg nr="2"> <not-uninit/> </arg> <arg nr="3"/> </function> </def>
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 <not-uninit> as far as values are concerned. Uninitialized memory might still be passed to the function. Here is a minimal windows.cfg file: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"> <not-null/> </arg> <arg nr="2"/> <arg nr="3"/> </function> </def>
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: <?xml version="1.0"?> <def> <function name="do_something"> <formatstr type="printf"/> <arg nr="1"> <formatstr/> </arg> </function> </def>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: <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <valid>0:1023</valid> </arg> </function> </def>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
minsize 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: <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <minsize type="strlen" arg="2"/> </arg> <arg nr="2"/> </function> </def>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 memccpy 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 This setting is not used by Cppcheck currently. But with this you can say that an argument must be a zero-terminated string. <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <strz/> </arg> </function> </def>
noreturn 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 <noreturn> 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: <?xml version="1.0"?> <def> <function name="ZeroMemory"> <noreturn>false</noreturn> <arg nr="1"/> <arg nr="2"/> </function> </def>
use-retval 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: <?xml version="1.0"?> <def> <function name="strcmp"> <use-retval/> <arg nr="1"/> <arg nr="2"/> </function> </def>
pure and const These correspond to the GCC function attributes pure and const. 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: <?xml version="1.0"?> <def> <function name="calculate"> <const/> <arg nr="1"/> </function> </def>
Example configuration for strcpy() The proper configuration for the standard strcpy() function would be: <function name="strcpy"> <leak-ignore/> <noreturn>false</noreturn> <arg nr="1"> <not-null/> </arg> <arg nr="2"> <not-null/> <not-uninit/> <strz/> </arg> </function> The <leak-ignore/> 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 <noreturn> 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 <not-null> is used. The second argument the function takes is a pointer. It must not be null. And it must point at initialized data. Using <not-null> and <not-uninit> is correct. Moreover it must point at a zero-terminated string so <strz> is also used.
define Libraries can be used to define preprocessor macros as well. For example: <?xml version="1.0"?> <def> <define name="NULL_VALUE" value="0"/> </def> Each occurrence of "NULL_VALUE" in the code would then be replaced by "0" at preprocessor stage.
podtype 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: <?xml version="1.0"?> <def> <podtype name="uint16_t" sign="u" size="2"/> </def> 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
container 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. Inside the <container> tag, functions can be defined inside of the tags <size>, <access> and <other> (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): <?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 You can define custom rules using regular expressions. These rules can not perform sophisticated analysis of the code. But they give you an easy way to check for various simple patterns in the code. To get started writing rules, see the related articles here: http://sourceforge.net/projects/cppcheck/files/Articles/ The file format for rules is: <?xml version="1.0"?> <rule> <tokenlist>LIST</tokenlist> <pattern>PATTERN</pattern> <message> <id>ID</id> <severity>SEVERITY</severity> <summary>SUMMARY</summary> </message> </rule> CDATA can be used to include characters in a pattern that might interfere with XML: <![CDATA[some<strange>pattern]]>
<tokenlist> The <tokenlist> element is optional. With this element you can control what tokens are checked. The LIST can be either define, raw, normal or simple. define used to check #define preprocessor statements. raw used to check the preprocessor output. normal used to check the normal token list. There are some simplifications. simple used to check the simple token list. All simplifications are used. Most Cppcheck checks use the simple token list. If there is no <tokenlist> element then simple is used automatically.
<pattern> The PATTERN is the PCRE-compatible regular expression that will be executed.
<id> The ID specify the user-defined message id.
<severity> The SEVERITY must be one of the Cppcheck severities: information, performance, portability, style, warning, or error.
<summary> Optional. The summary for the message. If no summary is given, the matching tokens is written.
Cppcheck addons Cppcheck addons are implemented as standalone scripts or programs. With Cppcheck addons, you can for instance: add extra custom checkers that use sophisticated analysis visualize your code etc
Using Cppcheck addons Currently there are two steps to use an addon: Run Cppcheck to generate dump files Run the addon on the dump files The --dump flag is used to generate dump files. To generate a dump file for every source file in the foo/ folder: cppcheck --dump foo/ To run a addon script on all dump files in the foo/ folder: python addon.py foo/*.dump
Where to find some Cppcheck addons There are a few addons that can be downloaded. Addons provided by the Cppcheck project: http://github.com/danmar/cppcheck/blob/master/addons ublinter, a project that wants to "lint" for "undefined behaviour": http://github.com/danmar/ublinter We would be happy to add a link to your addon here (no matter if it's commercial or free).
Writing Cppcheck addons Cppcheck generates dump files in XML format that contains: Token list Syntax trees Symbol database (functions, classes, variables, all scopes, ..) Known values (value flow analysis) Cppcheck can't execute addons directly. There is no direct interface. This means there are not much restrictions: You can use any licensing you want for your addons You can use an arbitrary script/programming language to write addons The user interface and output is defined by you You can use addons for other use cases than generating warnings For your convenience, Cppcheck provides cppcheckdata.py that you can use to access Cppcheck data from Python. Using this is optional.
Example 1 - print all tokens 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))
Example 2 - List all functions 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))
Example 3 - List all classes 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 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=. Graphical user interface
Introduction A Cppcheck GUI is available. The main screen is shown immediately when the GUI is started.
Check source code Use the Check menu.
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.
Settings The language can be changed at any time by using the Language menu. More settings are available in Edit Preferences .
Project files The project files are used to store project specific settings. These settings are: include folders preprocessor defines 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.82/man/writing-rules-1.docbook000066400000000000000000000106071322667425100203260ustar00rootroot00000000000000
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.82/man/writing-rules-2.docbook000066400000000000000000000201041322667425100203200ustar00rootroot00000000000000
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.82/man/writing-rules-3.docbook000066400000000000000000000164751322667425100203410ustar00rootroot00000000000000
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.82/platforms/000077500000000000000000000000001322667425100152435ustar00rootroot00000000000000cppcheck-1.82/platforms/avr8.xml000066400000000000000000000006011322667425100166420ustar00rootroot00000000000000 8 unsigned 1 2 2 4 8 4 4 4 2 2 2 cppcheck-1.82/platforms/msp430_eabi_large_datamodel.xml000066400000000000000000000005571322667425100231660ustar00rootroot00000000000000 8 signed 2 2 4 8 4 8 8 4 4 2 cppcheck-1.82/platforms/unix32-unsigned.xml000066400000000000000000000005621322667425100207320ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 12 4 4 2 cppcheck-1.82/platforms/unix64-unsigned.xml000066400000000000000000000005621322667425100207370ustar00rootroot00000000000000 8 unsigned 2 4 8 8 4 8 16 8 8 4 cppcheck-1.82/readme.md000066400000000000000000000077361322667425100150300ustar00rootroot00000000000000# **Cppcheck** |Linux Build Status|Windows Build Status|Coverity Scan Build Status| |:--:|:--:|:--:| |[![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)| ## Donations If you find Cppcheck useful for you, feel free to make a donation. [![Donate](http://pledgie.com/campaigns/4127.png)](http://pledgie.com/campaigns/4127) ## 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). ## 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 2010 then it will work. If nullptr is not supported by your compiler then this can be emulated using the header lib/cxx11emu.h. 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 2010 and above) * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (or later) * clang++ ### 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 2015, 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. ### 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 SRCDIR=build CFGDIR=cfg HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" ``` Flags: 1. `SRCDIR=build` Python is used to optimise cppcheck 2. `CFGDIR=cfg` Specify folder where .cfg files are found 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++0x -include lib/cxx11emu.h -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++0x -include lib/cxx11emu.h -lpcre -DHAVE_RULES -Ilib -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 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 ```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.82/readme.txt000066400000000000000000000064561322667425100152450ustar00rootroot00000000000000========= 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 2010 then it will work. If nullptr is not supported by your compiler then this can be emulated using the header lib/cxx11emu.h. 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++ 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 2015, 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. 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 SRCDIR=build CFGDIR=cfg HAVE_RULES=yes Flags: SRCDIR=build : Python is used to optimise cppcheck CFGDIR=cfg : Specify folder where .cfg files are found 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++0x -include lib/cxx11emu.h -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++0x -include lib/cxx11emu.h -lpcre -DHAVE_RULES -Ilib -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.82/rules/000077500000000000000000000000001322667425100143665ustar00rootroot00000000000000cppcheck-1.82/rules/empty-catch-block.xml000066400000000000000000000003441322667425100204170ustar00rootroot00000000000000 normal style Empty catch block found. cppcheck-1.82/rules/error-reporting.xml000066400000000000000000000004631322667425100202530ustar00rootroot00000000000000 Severity :: fromString \( "\w+" \) ConstantSeverityFromString style Constant severity lookups should be done via Severity::constant. cppcheck-1.82/rules/show-all-defines.rule000066400000000000000000000003511322667425100204170ustar00rootroot00000000000000 define .* showalldefines information cppcheck-1.82/rules/stl.xml000066400000000000000000000005541322667425100157160ustar00rootroot00000000000000 \. find \( "[^"]+?" \) == \d+ UselessSTDStringFind performance When looking for a string at a fixed position compare is faster. cppcheck-1.82/rules/strlen-empty-str.xml000066400000000000000000000005001322667425100203540ustar00rootroot00000000000000 if \( ([!] )*?(strlen) \( \w+? \) ([>] [0] )*?\) { StrlenEmptyString performance Using strlen() to check if a string is empty is not efficient. cppcheck-1.82/rules/suggest_nullptr.xml000066400000000000000000000006071322667425100203540ustar00rootroot00000000000000 raw modernizeUseNullPtr style Prefer to use a 'nullptr' instead of initialize a pointer with 0. cppcheck-1.82/rules/token-matching.xml000066400000000000000000000020421322667425100200160ustar00rootroot00000000000000 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.82/rules/unused-deref.xml000066400000000000000000000004221322667425100174740ustar00rootroot00000000000000 [;{}] [*] \w+? (\+\+|\-\-) ; UnusedDeref style Redundant * found, "*p++" is the same as "*(p++)". cppcheck-1.82/runastyle000077500000000000000000000033671322667425100152210ustar00rootroot00000000000000#!/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="Artistic Style Version 3.0.1" ASTYLE="astyle" DETECTED_VERSION=`$ASTYLE --version 2>&1` if [[ "$DETECTED_VERSION" != ${ASTYLE_VERSION}* ]]; then echo "You should use: ${ASTYLE_VERSION}"; echo "Detected: ${DETECTED_VERSION}" exit 1; fi style="--style=kr --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0" options="--options=none --pad-header --unpad-paren --suffix=none --convert-tabs --attach-inlines --attach-classes --attach-namespaces" $ASTYLE $style $options cli/*.cpp $ASTYLE $style $options cli/*.h $ASTYLE $style $options democlient/*.cpp $ASTYLE $style $options gui/*.cpp $ASTYLE $style $options gui/*.h $ASTYLE $style $options -r gui/test/*.cpp $ASTYLE $style $options -r gui/test/*.h $ASTYLE $style $options lib/*.cpp $ASTYLE $style $options lib/*.h $ASTYLE $style $options test/*.cpp $ASTYLE $style $options test/cfg/*.c* $ASTYLE $style $options test/*.h $ASTYLE $style $options --recursive "tools/*.cpp" $ASTYLE $style $options --recursive "tools/*.h" $ASTYLE $style $options --recursive "samples/*.c" $ASTYLE $style $options --recursive "samples/*.cpp" # Convert tabs to spaces.. even in strings # sed -i "s/\t/ /g" test/test*.cpp # format config files # TODO: use other tool than xmllint? use tabs instead of spaces? for CFGFILE in cfg/*.cfg do xmllint --format -o ${CFGFILE}_ ${CFGFILE} mv -f ${CFGFILE}_ ${CFGFILE} done cppcheck-1.82/runastyle.bat000066400000000000000000000015721322667425100157570ustar00rootroot00000000000000@REM Script to run AStyle on the sources @SET STYLE=--style=kr --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0 @SET OPTIONS=--pad-header --unpad-paren --suffix=none --convert-tabs --attach-inlines --attach-classes --attach-namespaces astyle %STYLE% %OPTIONS% cli/*.cpp astyle %STYLE% %OPTIONS% cli/*.h astyle %STYLE% %OPTIONS% democlient/*.cpp astyle %STYLE% %OPTIONS% gui/*.cpp astyle %STYLE% %OPTIONS% gui/*.h astyle %STYLE% %OPTIONS% -r gui/test/*.cpp astyle %STYLE% %OPTIONS% -r gui/test/*.h astyle %STYLE% %OPTIONS% lib/*.cpp astyle %STYLE% %OPTIONS% lib/*.h astyle %STYLE% %OPTIONS% test/*.cpp astyle %STYLE% %OPTIONS% test/cfg/*.cpp astyle %STYLE% %OPTIONS% test/*.h astyle %STYLE% %OPTIONS% -r tools/*.cpp astyle %STYLE% %OPTIONS% -r tools/*.h astyle %STYLE% %OPTIONS% -r samples/*.c astyle %STYLE% %OPTIONS% -r samples/*.cpp cppcheck-1.82/samples/000077500000000000000000000000001322667425100147005ustar00rootroot00000000000000cppcheck-1.82/samples/AssignmentAddressToInteger/000077500000000000000000000000001322667425100221375ustar00rootroot00000000000000cppcheck-1.82/samples/AssignmentAddressToInteger/bad.c000066400000000000000000000001401322667425100230240ustar00rootroot00000000000000int foo(int *p) { int a = p; return a + 4; } int main() { int i[10]; foo(i); } cppcheck-1.82/samples/AssignmentAddressToInteger/good.c000066400000000000000000000001221322667425100232260ustar00rootroot00000000000000int* foo(int *p) { return p + 4; } int main() { int i[10]; foo(i); } cppcheck-1.82/samples/AssignmentAddressToInteger/out.txt000066400000000000000000000001571322667425100235120ustar00rootroot00000000000000[samples\AssignmentAddressToInteger\bad.c:3]: (portability) Assigning a pointer to an integer is not portable. cppcheck-1.82/samples/arrayIndexOutOfBounds/000077500000000000000000000000001322667425100211365ustar00rootroot00000000000000cppcheck-1.82/samples/arrayIndexOutOfBounds/bad.c000066400000000000000000000001301322667425100220220ustar00rootroot00000000000000int main() { int a[2]; a[0] = 0; a[1] = 0; a[2] = 0; return a[0]; } cppcheck-1.82/samples/arrayIndexOutOfBounds/good.c000066400000000000000000000001301322667425100222240ustar00rootroot00000000000000int main() { int a[3]; a[0] = 0; a[1] = 0; a[2] = 0; return a[0]; } cppcheck-1.82/samples/arrayIndexOutOfBounds/out.txt000066400000000000000000000001531322667425100225050ustar00rootroot00000000000000[samples\arrayIndexOutOfBounds\bad.c:6]: (error) Array 'a[2]' accessed at index 2, which is out of bounds. cppcheck-1.82/samples/autoVariables/000077500000000000000000000000001322667425100175015ustar00rootroot00000000000000cppcheck-1.82/samples/autoVariables/bad.c000066400000000000000000000001331322667425100203700ustar00rootroot00000000000000void foo(int **a) { int b = 1; *a = &b; } int main() { int *c; foo(&c); } cppcheck-1.82/samples/autoVariables/good.c000066400000000000000000000001531322667425100205740ustar00rootroot00000000000000void foo(int **a) { int b = 1; **a = b; } int main() { int b; int *c = &b; foo(&c); } cppcheck-1.82/samples/autoVariables/out.txt000066400000000000000000000001521322667425100210470ustar00rootroot00000000000000[samples\autoVariables\bad.c:4]: (error) Address of local auto-variable assigned to a function parameter. cppcheck-1.82/samples/bufferAccessOutOfBounds/000077500000000000000000000000001322667425100214235ustar00rootroot00000000000000cppcheck-1.82/samples/bufferAccessOutOfBounds/bad.c000066400000000000000000000001471322667425100223170ustar00rootroot00000000000000int main() { int a[2]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-1.82/samples/bufferAccessOutOfBounds/good.c000066400000000000000000000001471322667425100225210ustar00rootroot00000000000000int main() { int a[3]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-1.82/samples/bufferAccessOutOfBounds/out.txt000066400000000000000000000001551322667425100227740ustar00rootroot00000000000000[samples\bufferAccessOutOfBounds\bad.c:6]: (error) Array 'a[2]' accessed at index 2, which is out of bounds. cppcheck-1.82/samples/erase/000077500000000000000000000000001322667425100157775ustar00rootroot00000000000000cppcheck-1.82/samples/erase/bad.cpp000066400000000000000000000004611322667425100172320ustar00rootroot00000000000000#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.82/samples/erase/good.cpp000066400000000000000000000005261322667425100174360ustar00rootroot00000000000000#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.82/samples/erase/out.txt000066400000000000000000000001651322667425100173510ustar00rootroot00000000000000[samples\erase\bad.cpp:9] -> [samples\erase\bad.cpp:11]: (error) Iterator 'iter' used after element has been erased. cppcheck-1.82/samples/memleak/000077500000000000000000000000001322667425100163135ustar00rootroot00000000000000cppcheck-1.82/samples/memleak/bad.c000066400000000000000000000002011322667425100171760ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; return result; } cppcheck-1.82/samples/memleak/good.c000066400000000000000000000002161322667425100174060ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; free(a); return result; } cppcheck-1.82/samples/memleak/out.txt000066400000000000000000000000621322667425100176610ustar00rootroot00000000000000[samples\memleak\bad.c:8]: (error) Memory leak: a cppcheck-1.82/samples/outOfBounds/000077500000000000000000000000001322667425100171475ustar00rootroot00000000000000cppcheck-1.82/samples/outOfBounds/bad.c000066400000000000000000000001301322667425100200330ustar00rootroot00000000000000#include int main() { char str[5]; strcpy(str, "0123456789abcdef"); } cppcheck-1.82/samples/outOfBounds/good.c000066400000000000000000000001301322667425100202350ustar00rootroot00000000000000#include int main() { char str[10]; snprintf(str, 10, "%s", "abc"); } cppcheck-1.82/samples/outOfBounds/out.txt000066400000000000000000000001151322667425100205140ustar00rootroot00000000000000[samples\outOfBounds\bad.c:5]: (error) Buffer is accessed out of bounds: str cppcheck-1.82/samples/resourceLeak/000077500000000000000000000000001322667425100173245ustar00rootroot00000000000000cppcheck-1.82/samples/resourceLeak/bad.c000066400000000000000000000001631322667425100202160ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; return 0; } cppcheck-1.82/samples/resourceLeak/good.c000066400000000000000000000002011322667425100204110ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; fclose(a); return 0; } cppcheck-1.82/samples/resourceLeak/out.txt000066400000000000000000000000711322667425100206720ustar00rootroot00000000000000[samples\resourceLeak\bad.c:8]: (error) Resource leak: a cppcheck-1.82/samples/syntaxError/000077500000000000000000000000001322667425100172405ustar00rootroot00000000000000cppcheck-1.82/samples/syntaxError/bad.c000066400000000000000000000000371322667425100201320ustar00rootroot00000000000000int main() { #ifdef A } #endif cppcheck-1.82/samples/syntaxError/good.c000066400000000000000000000000401322667425100203260ustar00rootroot00000000000000int main() { #ifndef A #endif } cppcheck-1.82/samples/syntaxError/out.txt000066400000000000000000000001431322667425100206060ustar00rootroot00000000000000[samples\syntaxError\bad.c:2]: (error) Invalid number of character '{' when no macros are defined. cppcheck-1.82/test/000077500000000000000000000000001322667425100142135ustar00rootroot00000000000000cppcheck-1.82/test/CMakeLists.txt000066400000000000000000000016601322667425100167560ustar00rootroot00000000000000if (BUILD_TESTS) 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") add_dependencies(testrunner copy_cfg) add_test(NAME testrunner COMMAND testrunner WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() cppcheck-1.82/test/cfg/000077500000000000000000000000001322667425100147525ustar00rootroot00000000000000cppcheck-1.82/test/cfg/gnu.c000066400000000000000000000016401322667425100157100ustar00rootroot00000000000000 // 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 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); // cppcheck-suppress knownConditionTrueFalse // cppcheck-suppress duplicateExpression if (42 == __builtin_expect(42, 0)) return; } cppcheck-1.82/test/cfg/posix.c000066400000000000000000000135201322667425100162610ustar00rootroot00000000000000 // 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 // unavailable on some linux systems #include #include #include #include #include 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; } void nullPointer(char *p, int fd) { // 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); } 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 } /* TODO: add configuration for fdopen void resourceLeak_fdopen(int fd) { FILE *f = fdopen(fd, "r"); // cppcheck-suppress resourceLeak } */ 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 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); /* 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(); } // 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 uninitvar(int fd) { int x; char buf[2]; int decimal, sign; double d; void *p; // cppcheck-suppress uninitvar write(x,"ab",2); // TODO cppcheck-suppress uninitvar write(fd,buf,2); // #6325 // cppcheck-suppress uninitvar write(fd,"ab",x); // 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 cflags; // cppcheck-suppress uninitvar regcomp(®, pattern, cflags); pattern=""; // cppcheck-suppress uninitvar regcomp(®, pattern, cflags); regerror(0, ®, 0, 0); // cppcheck-suppress uninitvar // cppcheck-suppress unreadVariable // cppcheck-suppress ecvtCalled char *buffer = ecvt(d, 11, &decimal, &sign); // 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); } 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; // cppcheck-suppress uninitvar d.d_ino + 1; } void timet_h(struct timespec* ptp1) { clockid_t clk_id; struct timespec* ptp; // cppcheck-suppress uninitvar clock_settime(CLOCK_REALTIME, ptp); // cppcheck-suppress uninitvar clock_settime(clk_id, ptp); // cppcheck-suppress uninitvar clock_settime(clk_id, ptp1); struct timespec tp; // TODO cppcheck-suppress uninitvar clock_settime(CLOCK_REALTIME, &tp); // #6577 - false negative // cppcheck-suppress uninitvar clock_settime(clk_id, &tp); time_t clock = time(0); char buf[26]; // cppcheck-suppress ctime_rCalled ctime_r(&clock, buf); } cppcheck-1.82/test/cfg/qt.cpp000066400000000000000000000011661322667425100161060ustar00rootroot00000000000000 // 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 // class QString { public: int size(); char &operator[](int pos); }; void QString1(QString s) { for (int i = 0; i <= s.size(); ++i) { // cppcheck-suppress stlOutOfBounds s[i] = 'x'; } } int QString2() { QString s; // cppcheck-suppress reademptycontainer return s.size(); } cppcheck-1.82/test/cfg/runtests.sh000077500000000000000000000022621322667425100172020ustar00rootroot00000000000000#!/bin/bash set -e # abort on error set -x # be verbose if [[ $(pwd) == */test/cfg ]] ; then # we are in test/cfg CPPCHECK="../../cppcheck" DIR="" else # assume we are in repo root CPPCHECK="./cppcheck" DIR=test/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=gnu ${DIR}gnu.c # qt.cpp ${CXX} ${CXX_OPT} ${DIR}qt.cpp ${CPPCHECK} --enable=style --enable=information --inconclusive --inline-suppr --error-exitcode=1 --library=qt ${DIR}qt.cpp # std.c ${CC} ${CC_OPT} ${DIR}std.c ${CPPCHECK} ${CPPCHECK_OPT} ${DIR}std.c ${CXX} ${CXX_OPT} ${DIR}std.cpp ${CPPCHECK} ${CPPCHECK_OPT} ${DIR}std.cpp cppcheck-1.82/test/cfg/std.c000066400000000000000000002104641322667425100157170ustar00rootroot00000000000000 // 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 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 // cppcheck-suppress redundantCopy sprintf(a, "ab%s", "cde"); // cppcheck-suppress redundantCopy snprintf(a, 5, "abcde%i", 1); // cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds snprintf(a, 6, "abcde%i", 1); // cppcheck-suppress redundantCopy strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds // cppcheck-suppress redundantCopy strcpy(a, "abcde"); // cppcheck-suppress redundantCopy strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds // cppcheck-suppress redundantCopy strncpy(a,"abcde",6); // cppcheck-suppress bufferAccessOutOfBounds // cppcheck-suppress redundantCopy strncpy(a,"a",6); // cppcheck-suppress redundantCopy strncpy(a,"abcdefgh",4); 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); } // 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; #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); // No FP // cppcheck-suppress redundantAssignment fp = freopen(0,"abc",stdin); fclose(fp); fp = 0; // 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 strlen(0); // cppcheck-suppress nullPointer strcpy(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcoll(0,0); // cppcheck-suppress nullPointer strcat(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcmp(0,0); // cppcheck-suppress nullPointer strncpy(0,0,1); // cppcheck-suppress nullPointer strncat(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strncmp(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strstr(0,0); // cppcheck-suppress nullPointer strtoul(0,0,0); // cppcheck-suppress nullPointer strtoull(0,0,0); // cppcheck-suppress nullPointer strtol(0,0,0); // #6100 False positive nullPointer - calling mbstowcs(NULL,) res += mbstowcs(0,"",0); // TODO cppcheck-suppress unreadVariable res += wcstombs(0,L"",0); strtok(NULL,"xyz"); strxfrm(0,"foo",0); // TODO: error message (#6306 and http://trac.cppcheck.net/changeset/d11eb4931aea51cf2cb74faccdcd2a3289b818d6/) strxfrm(0,"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); } void nullpointerMemchr1(char *p, char *s) { // cppcheck-suppress uselessAssignmentPtrArg p = memchr(s, 'p', strlen(s)); } void nullpointerMemchr2(char *p, char *s) { // cppcheck-suppress uselessAssignmentPtrArg p = memchr(s, 0, strlen(s)); } void nullPointer_memchr(char *p) { char *s = 0; // cppcheck-suppress nullPointer // cppcheck-suppress uselessAssignmentPtrArg p = memchr(s, 0, strlen(s)); } 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); } // uninit pointers void uninivar_abs(void) { int i; // cppcheck-suppress uninitvar (void)abs(i); } void uninit_clearerr(void) { FILE *fp; // cppcheck-suppress uninitvar clearerr(fp); } void uninit_fclose(void) { FILE *fp; // cppcheck-suppress uninitvar fclose(fp); } void uninit_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 uninit_feof(void) { FILE *fp; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress uninitvar feof(fp); } void uninit_ferror(void) { FILE *fp; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress uninitvar ferror(fp); } void uninit_fflush(void) { FILE *fp; // cppcheck-suppress uninitvar fflush(fp); } void uninit_fgetc(void) { FILE *fp; // cppcheck-suppress uninitvar (void)fgetc(fp); } void uninit_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 uninit_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 uninit_fgets(void) { FILE *fp; char buf[10]; char *str; fgets(buf,10,stdin); // cppcheck-suppress uninitvar fgets(str,10,stdin); // cppcheck-suppress uninitvar fgets(buf,10,fp); } void uninit_fputc(void) { int i; FILE *fp; fputc('a', stdout); // cppcheck-suppress uninitvar fputc(i, stdout); // cppcheck-suppress uninitvar fputc('a', fp); } void uninit_fputs(void) { const char *s; FILE *fp; fputs("a", stdout); // cppcheck-suppress uninitvar fputs(s, stdout); // cppcheck-suppress uninitvar fputs("a", fp); } void uninit_ftell(void) { FILE *fp; // cppcheck-suppress uninitvar (void)ftell(fp); } void uninit_puts(void) { const char *s; // cppcheck-suppress uninitvar puts(s); } void uninit_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); } #if 0 void uninitvar_assert(void) { int i; // TODO cppcheck-suppress uninitvar assert(i); } #endif 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_clearerr(void) { FILE * stream; // cppcheck-suppress uninitvar clearerr(stream); } 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_fclose(void) { FILE *stream; // cppcheck-suppress uninitvar (void)fclose(stream); } void uninitvar_feof(void) { FILE *stream; // cppcheck-suppress uninitvar (void)feof(stream); } void uninitvar_ferror(void) { FILE *stream; // cppcheck-suppress uninitvar (void)ferror(stream); } void uninitvar_fflush(void) { FILE *stream; // cppcheck-suppress uninitvar (void)fflush(stream); } void uninitvar_fgetc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)fgetc(stream); } void uninitvar_fgetwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)fgetwc(stream); } void uninitvar_fgetpos(void) { FILE* stream; fpos_t *ptr; // cppcheck-suppress uninitvar (void)fgetpos(stream,ptr); } 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 uninitar_fopen(void) { char *filename; char *mode; // cppcheck-suppress uninitvar FILE * fp = fopen(filename, mode); fclose(fp); } void uninitar_fprintf(FILE *Stream, char *Format, int Argument) { FILE *stream; char *format; int argument; // cppcheck-suppress uninitvar (void)fprintf(stream, format, argument); // cppcheck-suppress uninitvar (void)fprintf(stream, Format, Argument); // cppcheck-suppress uninitvar (void)fprintf(Stream, format, Argument); // cppcheck-suppress uninitvar (void)fprintf(Stream, Format, argument); // no warning is expected (void)fprintf(Stream, Format, Argument); } void uninitar_vfprintf(FILE *Stream, const char *Format, va_list Arg) { FILE *stream; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfprintf(stream, format, arg); // cppcheck-suppress uninitvar (void)vfprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)vfprintf(Stream, format, Arg); // no warning is expected (void)vfprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vfprintf(Stream, Format, arg); } void uninitar_vfwprintf(FILE *Stream, wchar_t *Format, va_list Arg) { FILE *stream; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfwprintf(stream, format, arg); // cppcheck-suppress uninitvar (void)vfwprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)vfwprintf(Stream, format, Arg); // no warning is expected (void)vfwprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vfwprintf(Stream, Format, arg); } void uninitvar_fputc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)fputc(c,stream); } void uninitvar_fputwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)fputwc(c,stream); } void uninitvar_fputs(void) { char *string; FILE *stream; // cppcheck-suppress uninitvar (void)fputs(string,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); free(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_fsetpos(void) { FILE* stream; fpos_t *ptr; // cppcheck-suppress uninitvar (void)fsetpos(stream,ptr); } void uninitvar_fgets(void) { char *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)fgets(buffer,n,stream); } void uninitvar_fgetws(void) { wchar_t *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)fgetws(buffer,n,stream); } void uninitvar_ftell(void) { FILE *stream; // cppcheck-suppress uninitvar (void)ftell(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 max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbrlen(pmb,max,ps); // cppcheck-suppress uninitvar (void)mbrlen(pmb,m,s); // cppcheck-suppress uninitvar (void)mbrlen(p,max,s); // cppcheck-suppress uninitvar (void)mbrlen(p,m,ps); // no warning is expected (void)mbrlen(p,m,s); } 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(void) { wchar_t *ws; char *s; size_t n; // cppcheck-suppress uninitvar (void)mbstowcs(ws,s,n); } void uninitvar_mbsrtowcs(void) { wchar_t* dest; const char* src; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbsrtowcs(dest,&src,max,ps); } 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 ignoreretrn(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 uninivar_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 uninivar_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 uninivar_printf(char *Format, int Argument) { char * format; int argument; // no warning is expected (void)printf("x"); // cppcheck-suppress uninitvar (void)printf(format,argument); // cppcheck-suppress uninitvar (void)printf(Format,argument); // cppcheck-suppress uninitvar (void)printf(format,Argument); // cppcheck-suppress uninitvar (void)printf(format,1); // no warning is expected (void)printf(Format,Argument); } void uninivar_vprintf(char *Format, va_list Arg) { char * format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vprintf(format,arg); // cppcheck-suppress uninitvar (void)vprintf(format,Arg); // no warning is expected (void)vprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vprintf(Format,arg); } void uninivar_vwprintf(wchar_t *Format, va_list Arg) { wchar_t * format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vwprintf(format,arg); // cppcheck-suppress uninitvar (void)vwprintf(format,Arg); // no warning is expected (void)vwprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vwprintf(Format,arg); } void uninivar_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_putchar(void) { int c; // cppcheck-suppress uninitvar (void)putchar(c); } void uninitvar_putwchar(void) { wchar_t c; // cppcheck-suppress uninitvar (void)putwchar(c); } void uninitvar_puts(void) { char *s; // cppcheck-suppress uninitvar (void)puts(s); } 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 uninivar_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 uninivar_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 uninivar_signal(void) { int i; // cppcheck-suppress uninitvar signal(i, exit); } void uninivar_raise(void) { int i; // cppcheck-suppress uninitvar (void)raise(i); } void uninivar_scanf(void) { char *format; char str[42]; // cppcheck-suppress uninitvar (void)scanf(format, str); } void uninivar_vsscanf(void) { char *s; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)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)vswscanf(s,format,arg); } void uninivar_vscanf(void) { char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vscanf(format,arg); } void uninivar_vwscanf(void) { wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vwscanf(format,arg); } void uninivar_setbuf(void) { FILE *stream; char *buf; // cppcheck-suppress uninitvar (void)setbuf(stream,buf); } void uninivar_setvbuf(void) { FILE *stream; char *buf; int mode; size_t size; // cppcheck-suppress uninitvar (void)setvbuf(stream,buf,mode,size); } void uninivar_strcat(void) { char *deststr; char *srcstr; // cppcheck-suppress uninitvar (void)strcat(deststr,srcstr); } void uninivar_wcscat(void) { wchar_t *deststr; wchar_t *srcstr; // cppcheck-suppress uninitvar (void)wcscat(deststr,srcstr); } void uninivar_wcrtomb(void) { char *s; wchar_t wc; mbstate_t *ps; // cppcheck-suppress uninitvar (void)wcrtomb(s,wc,ps); } void uninivar_strchr(void) { char *cs; int c; // cppcheck-suppress uninitvar (void)strchr(cs,c); } void uninivar_wcschr(void) { wchar_t *cs; wchar_t c; // cppcheck-suppress uninitvar (void)wcschr(cs,c); } void uninivar_strcmp(void) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)strcmp(str1,str2); } void uninivar_wcscmp(void) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)wcscmp(str1,str2); } void uninivar_strcpy(void) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)strcpy(str1,str2); } void uninivar_wcscpy(void) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)wcscpy(str1,str2); } void uninivar_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 uninivar_strlen(void) { char *s; // cppcheck-suppress uninitvar (void)strlen(s); } void uninivar_wcslen(void) { wchar_t *s; // cppcheck-suppress uninitvar (void)wcslen(s); } void uninivar_strncpy(void) { char *s; char *ct; size_t n; // cppcheck-suppress uninitvar (void)strncpy(s,ct,n); } void uninivar_strpbrk(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strpbrk(cs,ct); } void uninivar_strncat(char *Ct, char *S, size_t N) { char *ct; char *s; size_t n; // cppcheck-suppress uninitvar (void)strncat(ct,s,n); // cppcheck-suppress uninitvar (void)strncat(ct,S,N); // cppcheck-suppress uninitvar (void)strncat(Ct,s,N); // cppcheck-suppress uninitvar (void)strncat(Ct,S,n); // no warning is expected for (void)strncat(Ct,S,N); } void uninivar_wcsncat(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct; wchar_t *s; size_t n; // cppcheck-suppress uninitvar (void)wcsncat(ct,s,n); // cppcheck-suppress uninitvar (void)wcsncat(ct,S,N); // cppcheck-suppress uninitvar (void)wcsncat(Ct,s,N); // cppcheck-suppress uninitvar (void)wcsncat(Ct,S,n); // no warning is expected for (void)wcsncat(Ct,S,N); } void uninivar_strncmp(char *Ct, char *S, size_t N) { char *ct; char *s; size_t n; // cppcheck-suppress uninitvar (void)strncmp(ct,s,n); // cppcheck-suppress uninitvar (void)strncmp(ct,S,N); // cppcheck-suppress uninitvar (void)strncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)strncmp(Ct,S,n); // no warning is expected for (void)strncmp(Ct,S,N); } void uninivar_wcsncmp(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct; wchar_t *s; size_t n; // cppcheck-suppress uninitvar (void)wcsncmp(ct,s,n); // cppcheck-suppress uninitvar (void)wcsncmp(ct,S,N); // cppcheck-suppress uninitvar (void)wcsncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)wcsncmp(Ct,S,n); // no warning is expected for (void)wcsncmp(Ct,S,N); } void uninivar_strstr(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strstr(cs,ct); } void uninivar_wcsstr(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcsstr(cs,ct); } void uninivar_strspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strspn(cs,ct); } void uninivar_strxfrm(void) { char *ds; char *ss; size_t n; // cppcheck-suppress uninitvar (void)strxfrm(ds,ss,n); } void uninivar_wcsxfrm(void) { wchar_t *ds; wchar_t *ss; size_t n; // cppcheck-suppress uninitvar (void)wcsxfrm(ds,ss,n); } void uninivar_wcsspn(void) { wchar_t *ds; wchar_t *ss; // cppcheck-suppress uninitvar (void)wcsspn(ds,ss); } void uninivar_setlocale(void) { int category; char* locale; // cppcheck-suppress uninitvar (void)setlocale(category,locale); } void uninivar_strerror(void) { int i; // cppcheck-suppress uninitvar (void)strerror(i); } void uninivar_strcspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strcspn(cs,ct); } void uninivar_wcscspn(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcscspn(cs,ct); } void uninivar_wcspbrk(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcspbrk(cs,ct); } void uninivar_wcsncpy(void) { wchar_t *cs; wchar_t *ct; size_t n; // cppcheck-suppress uninitvar (void)wcsncpy(cs,ct,n); } void uninivar_strcoll(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strcoll(cs,ct); } void uninivar_wcscoll(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcscoll(cs,ct); } void uninivar_strrchr(void) { char * str; int c; // cppcheck-suppress uninitvar (void)strrchr(str,c); } void uninivar_strbprk(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strbprk(cs,ct); } void uninivar_wcsrchr(void) { wchar_t* ws; wchar_t wc; // cppcheck-suppress uninitvar (void)wcsrchr(ws,wc); } void uninivar_wcsrtombs(void) { char *dst; const wchar_t * p;; size_t len; mbstate_t *ps; // cppcheck-suppress uninitvar (void)wcsrtombs(dst,&p,len,ps); } void uninivar_strtok(void) { char *s; char *ct; // cppcheck-suppress uninitvar (void)strtok(s,ct); } void uninivar_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 uninivar_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 uninivar_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 uninivar_tolower(void) { int c; // cppcheck-suppress uninitvar (void)tolower(c); } void uninivar_toupper(void) { int c; // cppcheck-suppress uninitvar (void)toupper(c); } void uninivar_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 uninivar_mbrtowc(void) { wchar_t* pwc; const char* pmb; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbrtowc(pwc,pmb,max,ps); } void uninivar_wcstok(void) { wchar_t *s; const wchar_t *ct; wchar_t **ptr; // cppcheck-suppress uninitvar (void)wcstok(s,ct,ptr); } void uninivar_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 uninivar_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 *format; int argument; // cppcheck-suppress uninitvar (void)wprintf(format,argument); // cppcheck-suppress uninitvar (void)wprintf(format); // cppcheck-suppress uninitvar (void)wprintf(Format,argument); // cppcheck-suppress uninitvar (void)wprintf(format,Argument); // no warning is expected (void)wprintf(Format,Argument); (void)wprintf(Format); } void uninitvar_sprintf(char *S, char *Format, int Argument) { char *s; const char *format; int argument; // cppcheck-suppress uninitvar (void)sprintf(s,format,argument); // cppcheck-suppress uninitvar (void)sprintf(s,Format,Argument); // cppcheck-suppress uninitvar (void)sprintf(S,format,Argument); // cppcheck-suppress uninitvar (void)sprintf(S,Format,argument); // 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 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 uninivar_fwprintf(void) { FILE* stream; const wchar_t* format; int i; // cppcheck-suppress uninitvar (void)fwprintf(stream,format,i); } void uninivar_snprintf(char *S, size_t N, char *Format, int Int) { size_t n; char *format; int i; char *s; // cppcheck-suppress uninitvar (void)snprintf(s,n,format,i); // cppcheck-suppress uninitvar (void)snprintf(S,n,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,i); // i is uninitialized // TODO cppcheck-suppress uninitvar (void)snprintf(s,N,Format,Int); // no warning is expected for (void)snprintf(S,N,Format,Int); } void uninivar_vsnprintf(char *S, size_t N, char *Format, va_list Arg) { char *s; size_t n; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsnprintf(s,n,format,arg); // cppcheck-suppress uninitvar (void)vsnprintf(s,N,Format,Arg); // cppcheck-suppress uninitvar (void)vsnprintf(S,n,Format,Arg); // cppcheck-suppress uninitvar (void)vsnprintf(S,N,format,Arg); // no warning is expected for (void)vsnprintf(S,N,Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vsnprintf(S,N,Format,arg); } void uninivar_wscanf(void) { wchar_t *format; int i; // cppcheck-suppress uninitvar (void)wscanf(format); // cppcheck-suppress uninitvar (void)wscanf(format,&i); } void uninivar_sscanf(void) { char *string; const char * format; int i; // cppcheck-suppress uninitvar (void)sscanf(string,format); // cppcheck-suppress uninitvar (void)sscanf(string,format,&i); } void uninivar_fwscanf(void) { FILE* stream; wchar_t* format; int i; // cppcheck-suppress uninitvar (void)fwscanf(stream,format); // cppcheck-suppress uninitvar (void)fwscanf(stream,format,&i); } void uninivar_swscanf(void) { wchar_t* s; wchar_t* format; int i; // cppcheck-suppress uninitvar (void)swscanf(s,format); // cppcheck-suppress uninitvar (void)swscanf(s,format,&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 uninivar_c16rtomb(void) { char * pmb; char16_t c16; mbstate_t * ps; // cppcheck-suppress uninitvar (void)c16rtomb(pmb,c16,ps); } void uninivar_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 uninivar_c32rtomb(void) { char * pmb; char32_t c32; mbstate_t * ps; // cppcheck-suppress uninitvar (void)c32rtomb(pmb,c32,ps); } void uninivar_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 bufferAccessOutOfBounds(void) { char a[5]; std::strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds // cppcheck-suppress redundantCopy std::strcpy(a, "abcde"); // cppcheck-suppress redundantCopy std::strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds // 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 i; // cppcheck-suppress uninitvar (void)std::imaxabs(i); // cppcheck-suppress uninitvar (void)imaxabs(i); } 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 numer; intmax_t denom; // cppcheck-suppress uninitvar (void)std::imaxdiv(numer,denom); // cppcheck-suppress uninitvar (void)imaxdiv(numer,denom); } 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 *format; int argument; // cppcheck-suppress uninitvar (void)std::fprintf(stream, format, argument); // cppcheck-suppress uninitvar (void)std::fprintf(stream, Format, Argument); // cppcheck-suppress uninitvar (void)std::fprintf(Stream, format, Argument); // cppcheck-suppress uninitvar (void)std::fprintf(Stream, Format, argument); // no warning is expected (void)std::fprintf(Stream, Format, Argument); } void uninitar_vfprintf(FILE *Stream, const char *Format, va_list Arg) { FILE *stream; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfprintf(stream, format, arg); // cppcheck-suppress uninitvar (void)std::vfprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)std::vfprintf(Stream, format, 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 *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfwprintf(stream, format, arg); // cppcheck-suppress uninitvar (void)std::vfwprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)std::vfwprintf(Stream, format, 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 max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbrlen(pmb,max,ps); // cppcheck-suppress uninitvar (void)std::mbrlen(pmb,m,s); // cppcheck-suppress uninitvar (void)std::mbrlen(p,max,s); // cppcheck-suppress uninitvar (void)std::mbrlen(p,m,ps); // no warning is expected (void)std::mbrlen(p,m,s); } 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; int argument; // no warning is expected (void)std::printf("x"); // cppcheck-suppress uninitvar (void)std::printf(format,argument); // cppcheck-suppress uninitvar (void)std::printf(Format,argument); // cppcheck-suppress uninitvar (void)std::printf(format,Argument); // cppcheck-suppress uninitvar (void)std::printf(format,1); // no warning is expected (void)std::printf(Format,Argument); } void uninivar_vprintf(char *Format, va_list Arg) { char * format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vprintf(format,arg); // cppcheck-suppress uninitvar (void)std::vprintf(format,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 * format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vwprintf(format,arg); // cppcheck-suppress uninitvar (void)std::vwprintf(format,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 uninivar_strcat(void) { char *deststr; char *srcstr; // cppcheck-suppress uninitvar (void)std::strcat(deststr,srcstr); } void uninivar_wcscat(void) { wchar_t *deststr; wchar_t *srcstr; // cppcheck-suppress uninitvar (void)std::wcscat(deststr,srcstr); } 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; char *s; size_t n; // cppcheck-suppress uninitvar (void)std::strncat(ct,s,n); // cppcheck-suppress uninitvar (void)std::strncat(ct,S,N); // cppcheck-suppress uninitvar (void)std::strncat(Ct,s,N); // cppcheck-suppress uninitvar (void)std::strncat(Ct,S,n); // 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; wchar_t *s; size_t n; // cppcheck-suppress uninitvar (void)std::wcsncat(ct,s,n); // cppcheck-suppress uninitvar (void)std::wcsncat(ct,S,N); // cppcheck-suppress uninitvar (void)std::wcsncat(Ct,s,N); // cppcheck-suppress uninitvar (void)std::wcsncat(Ct,S,n); // 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 n; // 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,N); // cppcheck-suppress uninitvar (void)std::strncmp(Ct,S,n); // 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 n; // 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,N); // cppcheck-suppress uninitvar (void)std::wcsncmp(Ct,S,n); // 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* idx; // cppcheck-suppress uninitvar (void)std::stod(str,idx); // cppcheck-suppress uninitvar (void)std::stod(wstr,idx); // cppcheck-suppress uninitvar (void)std::stof(str,idx); // cppcheck-suppress uninitvar (void)std::stof(wstr,idx); // cppcheck-suppress uninitvar (void)std::stoi(str,idx); // cppcheck-suppress uninitvar (void)std::stoi(wstr,idx); // cppcheck-suppress uninitvar (void)std::stol(str,idx); // cppcheck-suppress uninitvar (void)std::stol(wstr,idx); // cppcheck-suppress uninitvar (void)std::stold(str,idx); // cppcheck-suppress uninitvar (void)std::stold(wstr,idx); // cppcheck-suppress uninitvar (void)std::stoll(str,idx); // cppcheck-suppress uninitvar (void)std::stoll(wstr,idx); // cppcheck-suppress uninitvar (void)std::stoul(str,idx); // cppcheck-suppress uninitvar (void)std::stoul(wstr,idx); // cppcheck-suppress uninitvar (void)std::stoull(str,idx); // cppcheck-suppress uninitvar (void)std::stoull(wstr,idx); } 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 argument; // cppcheck-suppress uninitvar (void)std::wprintf(format,argument); // cppcheck-suppress uninitvar (void)std::wprintf(format); // cppcheck-suppress uninitvar (void)std::wprintf(Format,argument); // 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 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 n; char *format; int i; char *s; // cppcheck-suppress uninitvar (void)std::snprintf(s,n,format,i); // cppcheck-suppress uninitvar (void)std::snprintf(S,n,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,i); // i is uninitialized // TODO cppcheck-suppress uninitvar (void)std::snprintf(s,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 *s; size_t n; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsnprintf(s,n,format,arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(s,N,Format,Arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(S,n,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* format; int i; // cppcheck-suppress uninitvar (void)std::fwscanf(stream,format); // cppcheck-suppress uninitvar (void)std::fwscanf(stream,format,&i); } void uninivar_swscanf(void) { wchar_t* s; wchar_t* format; int i; // cppcheck-suppress uninitvar (void)std::swscanf(s,format); // cppcheck-suppress uninitvar (void)std::swscanf(s,format,&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 pos; // cppcheck-suppress uninitvar (void)s.find("find",pos); // #6991 // testing of size_t find (const char* s, size_t pos = 0) const; char *pc; // cppcheck-suppress uninitvar (void)s.find(pc,0); // cppcheck-suppress uninitvar (void)s.find(pc,pos); // cppcheck-suppress uninitvar (void)s.find("test",pos); // testing of size_t find (char c, size_t pos = 0) const; char c; // cppcheck-suppress uninitvar (void)s.find(c,pos); // testing of size_t find (const char* pc, size_t pos, size_t n) const; size_t n; // cppcheck-suppress uninitvar (void)s.find(pc,pos,n); // #6991 // cppcheck-suppress uninitvar (void)s.find("test",pos,n); // cppcheck-suppress uninitvar (void)s.find("test",1,n); // cppcheck-suppress uninitvar (void)s.find("test",pos,1); // cppcheck-suppress uninitvar (void)s.find(pc,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 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()) {} // // 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(); } void stdvector() { std::vector v; // cppcheck-suppress ignoredReturnValue v.size(); } cppcheck-1.82/test/options.cpp000066400000000000000000000022171322667425100164140ustar00rootroot00000000000000// 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 "options.h" #include options::options(int argc, const char* argv[]) :_options(argv + 1, argv + argc) ,_which_test("") ,_quiet(_options.count("-q") != 0) { _options.erase("-q"); if (! _options.empty()) { _which_test = *_options.rbegin(); } } bool options::quiet() const { return _quiet; } const std::string& options::which_test() const { return _which_test; } cppcheck-1.82/test/options.h000066400000000000000000000027731322667425100160700ustar00rootroot00000000000000// 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 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* argv[]); /** Don't print the name of each method being tested. */ bool quiet() const; /** Which test should be run. Empty string means 'all tests' */ const std::string& which_test() const; private: options(); options(const options& non_copy); const options& operator =(const options& non_assign); private: std::set _options; std::string _which_test; const bool _quiet; }; #endif cppcheck-1.82/test/redirect.h000066400000000000000000000053671322667425100162000ustar00rootroot00000000000000// 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.82/test/synthetic/000077500000000000000000000000001322667425100162255ustar00rootroot00000000000000cppcheck-1.82/test/synthetic/Makefile000066400000000000000000000004631322667425100176700ustar00rootroot00000000000000ifndef 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.82/test/synthetic/controlflow.c000066400000000000000000000017611322667425100207460ustar00rootroot00000000000000 ////////////////////////////// // 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.82/test/synthetic/data.c000066400000000000000000000017341322667425100173070ustar00rootroot00000000000000 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.82/test/synthetic/functions.c000066400000000000000000000005571322667425100204100ustar00rootroot00000000000000 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.82/test/synthetic/report.py000077500000000000000000000033021322667425100201130ustar00rootroot00000000000000#!/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.82/test/synthetic/run-clang.sh000077500000000000000000000003641322667425100204550ustar00rootroot00000000000000~/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.82/test/synthetic/run-lint.bat000077500000000000000000000005621322667425100204730ustar00rootroot00000000000000\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.82/test/synthetic/ub.c000066400000000000000000000016631322667425100170050ustar00rootroot00000000000000void 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.82/test/test.cxx000066400000000000000000000007271322667425100157240ustar00rootroot00000000000000/* 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.82/test/test64bit.cpp000066400000000000000000000164771322667425100165660ustar00rootroot00000000000000/* * 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 "check64bit.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class Test64BitPortability : public TestFixture { public: Test64BitPortability() : TestFixture("Test64BitPortability") { } private: Settings settings; void run() { 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.82/test/testassert.cpp000066400000000000000000000171161322667425100171260ustar00rootroot00000000000000/* * 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 "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(&tokenizer, &settings, this); checkAssert.runSimplifiedChecks(&tokenizer, &settings, this); } void run() { 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.82/test/testastutils.cpp000066400000000000000000000074461322667425100175020ustar00rootroot00000000000000/* * 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 "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() { TEST_CASE(isReturnScope); TEST_CASE(isVariableChanged); TEST_CASE(isVariableChangedByFunctionCall); } 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("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 } 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); } void isVariableChanged() { // #8211 - no lhs for >> , do not crash ASSERT_EQUALS(true, isVariableChanged("void f() {\n" " int b;\n" " if (b) { (int)((INTOF(8))result >> b); }\n" "}", "if", "}")); } 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, &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); } }; REGISTER_TEST(TestAstUtils) cppcheck-1.82/test/testautovariables.cpp000066400000000000000000001132451322667425100204660ustar00rootroot00000000000000/* * 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 "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, bool runSimpleChecks = true, 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(&tokenizer, &settings, this); checkAutoVariables.returnReference(); checkAutoVariables.assignFunctionArg(); if (runSimpleChecks) { tokenizer.simplifyTokenList2(); // Check auto variables checkAutoVariables.autoVariables(); checkAutoVariables.returnPointerToLocalArray(); } } void run() { settings.addEnabled("warning"); settings.addEnabled("style"); LOAD_LIB_2(settings.library, "std.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(testautovar_array1); TEST_CASE(testautovar_array2); TEST_CASE(testautovar_ptrptr); // ticket #6956 TEST_CASE(testautovar_return1); TEST_CASE(testautovar_return2); TEST_CASE(testautovar_return3); TEST_CASE(testautovar_extern); TEST_CASE(testinvaliddealloc); TEST_CASE(testinvaliddealloc_C); TEST_CASE(testassign1); // Ticket #1819 TEST_CASE(testassign2); // Ticket #2765 TEST_CASE(returnLocalVariable1); TEST_CASE(returnLocalVariable2); TEST_CASE(returnLocalVariable3); // &x[0] TEST_CASE(returnLocalVariable4); // x+y TEST_CASE(returnLocalVariable5); // cast // 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(returnReferenceLiteral); TEST_CASE(returnReferenceCalculation); TEST_CASE(returnReferenceLambda); TEST_CASE(returnReferenceInnerScope); // global namespace TEST_CASE(testglobalnamespace); TEST_CASE(returnParameterAddress); TEST_CASE(testconstructor); // ticket #5478 - crash TEST_CASE(variableIsUsedInScope); // ticket #5599 crash in variableIsUsedInScope() } 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" "}", false); ASSERT_EQUALS("", errout.str()); check("void foo(struct AB *ab)\n" "{\n" " char a;\n" " ab->a = &a;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) 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(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,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()); // #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 check("void f(int* a) { a = }"); 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, false); 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_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]: (error) Address of an auto-variable returned.\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]: (error) Address of an auto-variable returned.\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_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 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()); } 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, true, "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 returnLocalVariable1() { check("char *foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Pointer to local array variable returned.\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:5]: (error) Pointer to local array variable returned.\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]: (error) Pointer to local array variable returned.\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]: (error) Pointer to local array variable returned.\n", errout.str()); check("char *foo()\n" "{\n" " static char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable4() { // x+y check("char *foo() {\n" " char x[10] = {0};\n" " return x+5;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); check("char *foo(int y) {\n" " char x[10] = {0};\n" " return (x+8)-y;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); } void returnLocalVariable5() { // cast check("char *foo() {\n" " int x[10] = {0};\n" " return (char *)x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Pointer to local array variable returned.\n", errout.str()); } void returnReference1() { check("std::string &foo()\n" "{\n" " std::string s;\n" " return s;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to auto 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 auto 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::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 auto 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 auto 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, 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 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()); } 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 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]: (error) Address of function parameter 'y' returned.\n", errout.str()); check("int ** foo(int * y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of function parameter 'y' returned.\n", errout.str()); check("const int * foo(const int & y)\n" "{\n" " return &y;\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" "}"); } }; REGISTER_TEST(TestAutoVariables) cppcheck-1.82/test/testbool.cpp000066400000000000000000001121561322667425100165600ustar00rootroot00000000000000/* * 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 "checkbool.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestBool : public TestFixture { public: TestBool() : TestFixture("TestBool") { } private: Settings settings; void run() { 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(checkComparisonOfFuncReturningBool1); TEST_CASE(checkComparisonOfFuncReturningBool2); TEST_CASE(checkComparisonOfFuncReturningBool3); TEST_CASE(checkComparisonOfFuncReturningBool4); TEST_CASE(checkComparisonOfFuncReturningBool5); TEST_CASE(checkComparisonOfFuncReturningBool6); TEST_CASE(checkComparisonOfBoolWithBool); // Converting pointer addition result to bool TEST_CASE(pointerArithBool1); } 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); tokenizer.simplifyTokenList2(); checkBool.runSimplifiedChecks(&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()); } 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 f(){\n" " int temp = 4;\n" " bool a = true;\n" " if(compare(temp) > a){\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", 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 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 variable '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 variable '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 variable '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 variable '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 variable '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 variable '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 variable '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 variable '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 variable '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 variable 'b' 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()); } 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(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 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()); } }; REGISTER_TEST(TestBool) cppcheck-1.82/test/testboost.cpp000066400000000000000000000072261322667425100167540ustar00rootroot00000000000000/* * 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 "checkboost.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestBoost : public TestFixture { public: TestBoost() : TestFixture("TestBoost") { } private: Settings settings; void run() { 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"); tokenizer.simplifyTokenList2(); // Check.. CheckBoost checkBoost; checkBoost.runSimplifiedChecks(&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.82/test/testbufferoverrun.cpp000066400000000000000000004331611322667425100205210ustar00rootroot00000000000000/* * 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 "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); tokenizer.simplifyTokenList2(); checkBufferOverrun.runSimplifiedChecks(&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); tokenizer.simplifyTokenList2(); checkBufferOverrun.runSimplifiedChecks(&tokenizer, &settings, this); } void run() { 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_6); TEST_CASE(array_index_7); TEST_CASE(array_index_9); 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_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_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(buffer_overrun_2_struct); TEST_CASE(buffer_overrun_3); TEST_CASE(buffer_overrun_4); TEST_CASE(buffer_overrun_5); TEST_CASE(buffer_overrun_6); TEST_CASE(buffer_overrun_7); TEST_CASE(buffer_overrun_8); TEST_CASE(buffer_overrun_9); TEST_CASE(buffer_overrun_10); TEST_CASE(buffer_overrun_11); TEST_CASE(buffer_overrun_15); // ticket #1787 TEST_CASE(buffer_overrun_16); TEST_CASE(buffer_overrun_18); // ticket #2576 - for, calculation with loop variable TEST_CASE(buffer_overrun_19); // #2597 - class member with unknown type TEST_CASE(buffer_overrun_21); TEST_CASE(buffer_overrun_24); // index variable is changed in for-loop TEST_CASE(buffer_overrun_26); // #4432 (segmentation fault) TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault) TEST_CASE(buffer_overrun_28); // Out of bound char array access TEST_CASE(buffer_overrun_29); // #7083: false positive: typedef and initialization with strings TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch TEST_CASE(buffer_overrun_function_array_argument); TEST_CASE(possible_buffer_overrun_1); // #3035 TEST_CASE(buffer_overrun_readSizeFromCfg); TEST_CASE(valueflow_string); // using ValueFlow string values in checking // It is undefined behaviour to point out of bounds of an array // the address beyond the last element is in bounds // char a[10]; // char *p1 = a + 10; // OK // char *p2 = a + 11 // UB TEST_CASE(pointer_out_of_bounds_1); TEST_CASE(pointer_out_of_bounds_2); TEST_CASE(pointer_out_of_bounds_sub); TEST_CASE(strncat1); TEST_CASE(strncat2); TEST_CASE(strncat3); TEST_CASE(strcat1); TEST_CASE(strcat2); TEST_CASE(strcat3); TEST_CASE(varid1); TEST_CASE(varid2); // ticket #4764 TEST_CASE(assign1); TEST_CASE(alloc_new); // Buffer allocated with new TEST_CASE(alloc_malloc); // Buffer allocated with malloc TEST_CASE(alloc_string); // statically allocated buffer TEST_CASE(alloc_alloca); // Buffer allocated with alloca 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(recursive_long_time); TEST_CASE(crash1); // Ticket #1587 - crash TEST_CASE(crash2); // Ticket #3034 - crash TEST_CASE(crash3); // Ticket #5426 - crash TEST_CASE(executionPaths1); TEST_CASE(executionPaths2); TEST_CASE(executionPaths3); // no FP for function parameter TEST_CASE(executionPaths5); // Ticket #2920 - False positive when size is unknown TEST_CASE(executionPaths6); // unknown types TEST_CASE(cmdLineArgs1); TEST_CASE(checkBufferAllocatedWithStrlen); TEST_CASE(scope); // handling different scopes TEST_CASE(getErrorMessages); // Access array and then check if the used index is within bounds TEST_CASE(arrayIndexThenCheck); TEST_CASE(bufferNotZeroTerminated); TEST_CASE(negativeMemoryAllocationSizeError) // #389 TEST_CASE(negativeArraySize); } 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()); } 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); }"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, 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;" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'x->str[1]' accessed at index 1, which is out of bounds.\n" "[test.cpp:10]: (error) Array 'x.str[1]' accessed at index 1, which is out of bounds.\n", 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("", 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" "[test.cpp:8]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_9() { check("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 'str[5]' accessed at index 10, which is out of bounds.\n", errout.str()); check("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 'str[5]' accessed at index 10, which is out of bounds.\n", errout.str()); check("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 check("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 check("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()); } 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" "[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 // CHAR_MAX can be equal to SCHAR_MAX or UCHAR_MAX depending on the environment. // This test should work for both environments. std::ostringstream charMaxPlusOne; charMaxPlusOne << (CHAR_MAX+1); check(("void f(char n) {\n" " int a[n];\n" // n <= CHAR_MAX " a[-1] = 0;\n" // negative index " a[" + charMaxPlusOne.str() + "] = 0;\n" // 128/256 > CHAR_MAX "}\n").c_str()); ASSERT_EQUALS("[test.cpp:3]: (error) Array index -1 is out of bounds.\n" "[test.cpp:4]: (error) Array 'a["+charMaxPlusOne.str()+"]' accessed at index "+charMaxPlusOne.str()+", 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 index -1 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 index -1 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 index -1 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 index -1 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 index -1 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 index -1 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 index -1 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 index -1 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 index -1 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" "}"); 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]' index y[0][2][0] out of bounds.\n" "[test.cpp:4]: (error) Array 'y[2][2][2]' index y[0][0][2] 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]' index test.b[10][2] out of bounds.\n" "[test.cpp:11]: (error) Array 'test.b[10][5]' index test.b[0][19] 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]' index ptest->b[10][2] out of bounds.\n" "[test.cpp:16]: (error) Array 'ptest->b[10][5]' index ptest->b[0][19] 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]' index ptest.b[10][2] out of bounds.\n" "[test.cpp:16]: (error) Array 'ptest.b[10][5]' index ptest.b[0][19] 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]' index test.a[9][5] out of bounds.\n" "[test.cpp:9]: (error) Array 'test.a[10][5]' index test.a[0][50] out of bounds.\n" "[test.cpp:12]: (error) Array 'ptest->a[10][5]' index ptest->a[9][5] out of bounds.\n" "[test.cpp:13]: (error) Array 'ptest->a[10][5]' index ptest->a[0][50] out of bounds.\n" "[test.cpp:12]: (error) Array 'ptest.a[10][5]' index ptest.a[9][5] out of bounds.\n" "[test.cpp:13]: (error) Array 'ptest.a[10][5]' index ptest.a[0][50] 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" "[test.cpp:10]: (error) Array 'var.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" "[test.cpp:9]: (error) Array 'var.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_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]' index a[2][1] 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]' index a[1][2] 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]' index a[2][1][1] 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]' index a[1][2][1] 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]' index a[1][2][1][1] 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]' index a[1][1][2] 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]' index a[6][12][2] 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]' index a[6][40][10] 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]' index a[2][2][2] 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]' index a[6][6][2] 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 index -1 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 index -1 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_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 index -9994 is out of bounds.\n" "[test.cpp:5]: (error) Array index -9995 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 index -1 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 index -1 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" "}"); 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" "[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:3] -> [test.cpp:4]: (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:3] -> [test.cpp:5]: (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};", false, "test.cpp"); ASSERT_EQUALS("", errout.str()); check("namespace { class X { static const int x[100]; };\n" // #6232 "const int X::x[100] = {0}; }", false, "test.cpp"); 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" "}"); 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" "}"); 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" "}"); 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()); } void buffer_overrun_28() { check("char c = \"abc\"[4];"); ASSERT_EQUALS("[test.cpp:1]: (error) Buffer is accessed out of bounds: \"abc\"\n", errout.str()); check("p = &\"abc\"[4];"); ASSERT_EQUALS("", errout.str()); check("char c = \"\\0abc\"[2];"); 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()); } 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);" "}"); 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: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()); } 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_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" "[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()); } // 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()); } // 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; unknownParameter.push_back(0); 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(0); std::list stringAsParameter; stringAsParameter.push_back(&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)); std::list intAsParameter; Token numTok(0); numTok.str("12345"); intAsParameter.push_back(&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)); std::list floatAsParameter; Token floatTok(0); floatTok.str("1.12345f"); floatAsParameter.push_back(&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)); std::list floatAsParameter2; Token floatTok2(0); floatTok2.str("100.12345f"); floatAsParameter2.push_back(&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; multipleParams.push_back(&strTok); multipleParams.push_back(0); multipleParams.push_back(&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"); 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); 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); 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, 10);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", 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()); // #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); 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 " const 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("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); 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); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(int condition) {\n" " char str[5];\n" " mysprintf(str, \"test%s\", condition ? \"12\" : \"34\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f(int condition) {\n" " char str[5];\n" " mysprintf(str, \"test%s\", condition ? \"12\" : \"345\");\n" "}", settings); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo x;\n" " mysprintf(x.a, \"aa\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:4]: (error) 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); 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); 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("error", "", 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("[test.cpp:3]: (warning, inconclusive) The buffer 'baz' may not be null-terminated after the call to strncpy().\n", 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 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 executionPaths1() { check("void f(int a)\n" "{\n" " int buf[10];\n" " int i = 5;\n" " if (a == 1)\n" " i = 1000;\n" " buf[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'buf[10]' accessed at index 1000, which is out of bounds.\n", errout.str()); check("void f(int a)\n" "{\n" " int buf[10][5];\n" " int i = 5;\n" " if (a == 1)\n" " i = 1000;\n" " buf[i][0] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'buf[10][5]' index buf[1000][0] out of bounds.\n", errout.str()); } void executionPaths2() { check("void foo()\n" "{\n" " char a[64];\n" " int sz = sizeof(a);\n" " bar(&sz);\n" " a[sz] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void executionPaths3() { check("void f(char *VLtext)\n" "{\n" " if ( x ) {\n" " return VLtext[0];\n" " } else {\n" " int wordlen = ab();\n" " VLtext[wordlen] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void executionPaths5() { // No false positive check("class A {\n" " void foo() {\n" " int j = g();\n" " arr[j]=0;\n" " }\n" "\n" " int arr[2*BSize + 2];\n" "};"); ASSERT_EQUALS("", errout.str()); } void executionPaths6() { // handling unknown type const char code[] = "void f() {\n" " u32 a[10];" " u32 i = 0;\n" " if (x) { i = 1000; }\n" " a[i] = 0;\n" "}"; check(code); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 1000, which is out of bounds.\n", errout.str()); } void cmdLineArgs1() { check("int main(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, 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, 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, 0); } 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()); } 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); }"); 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()); } }; REGISTER_TEST(TestBufferOverrun) cppcheck-1.82/test/testcharvar.cpp000066400000000000000000000144571322667425100172600ustar00rootroot00000000000000/* * 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 "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() { 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 = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Signed 'char' type used as array index.\n", 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.82/test/testclass.cpp000066400000000000000000010054661322667425100167400ustar00rootroot00000000000000/* * 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 "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() { settings0.addEnabled("style"); settings1.addEnabled("warning"); 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(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(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(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(initializerListOrder); TEST_CASE(initializerListUsage); TEST_CASE(selfInitialization); TEST_CASE(pureVirtualFunctionCall); TEST_CASE(pureVirtualFunctionCallOtherClass); TEST_CASE(pureVirtualFunctionCallWithBody); TEST_CASE(pureVirtualFunctionCallPrevented); TEST_CASE(duplInheritedMembers); TEST_CASE(explicitConstructors); TEST_CASE(copyCtorAndEqOperator); TEST_CASE(unsafeClassDivZero); } 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" "};"); ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'copy constructor' but lack of 'operator='.\n", errout.str()); checkCopyCtorAndEqOperator("class A \n" "{ \n" " A& operator=(const A& other) { return *this; }\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'operator=' but lack of 'copy constructor'.\n", errout.str()); 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()); } 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()); } 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:6] -> [test.cpp:3]: (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:6] -> [test.cpp:3]: (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:9] -> [test.cpp:3]: (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:10] -> [test.cpp:3]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n" "[test.cpp:10] -> [test.cpp:7]: (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" "};"); ASSERT_EQUALS("[test.cpp:5]: (style) 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" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (style) 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]: (style) 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" "};"); TODO_ASSERT_EQUALS("[test.cpp:5]: (style) 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]: (style) 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" "};"); 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" "};"); 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" "};"); 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" "};"); 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" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) class 'F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\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" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F : E\n" "{\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\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" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class E {};\n" "class F : E {\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:2]: (style) 'class F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\n", "", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " F(F& f);\n" // non-copyable "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F() : p(malloc(100)) {}\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) class 'F' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.\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" " _Tp* _M_finish;\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()); } 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()); } 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()); } 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 => no error checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : private Fred, public Base { };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); } void virtualDestructor5() { // Derived class has empty destructor => no error checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() {} };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", 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("", 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 method.\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 method.\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 method.\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" ""; 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::uint8_t b;\n" " std::uint8_t c;\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 method.\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 = 0, 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.\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.\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.\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.\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.\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.\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.\n" "[test.cpp:10] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo2' can be static.\n" "[test.cpp:11] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::foo3' can be static.\n" "[test.cpp:12] -> [test.cpp:6]: (performance, inconclusive) Technically the member function 'Fred::foo4' can be static.\n" "[test.cpp:13] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::foo5' can be static.\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 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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\n" "[test.cpp:7]: (performance, inconclusive) Technically the member function 'Altren::fun2' can be static.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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", 0, 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 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.\n" "[test.cpp:14]: (performance, inconclusive) Technically the member function 'Foo::bar4' can be static.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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.\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 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()); } 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 checkPureVirtualFunctionCall(const char code[], Settings *s = 0, 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.checkPureVirtualFunctionCall(); } void pureVirtualFunctionCall() { checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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 checkPureVirtualFunctionCall("class abc {\n" "public:\n" " virtual ~abc() throw() {}\n" " virtual void def(void* g) throw () = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #4992 checkPureVirtualFunctionCall("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() { checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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() { checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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() { checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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()); checkPureVirtualFunctionCall("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 checkUnsafeClassDivZero(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.checkUnsafeClassDivZero(true); } void unsafeClassDivZero() { checkUnsafeClassDivZero("class A {\n" "public:\n" " void dostuff(int x);\n" "}\n" "void A::dostuff(int x) { int a = 1000 / x; }"); ASSERT_EQUALS("[test.cpp:5]: (style) Public interface of A is not safe. When calling A::dostuff(), if parameter x is 0 that leads to division by zero.\n", errout.str()); checkUnsafeClassDivZero("class A {\n" "public:\n" " void f1();\n" " void f2(int x);\n" "}\n" "void A::f1() {}\n" "void A::f2(int x) { int a = 1000 / x; }"); ASSERT_EQUALS("[test.cpp:7]: (style) Public interface of A is not safe. When calling A::f2(), if parameter x is 0 that leads to division by zero.\n", errout.str()); checkUnsafeClassDivZero("class A {\n" "public:\n" " void operator/(int x);\n" "}\n" "void A::operator/(int x) { int a = 1000 / x; }"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestClass) cppcheck-1.82/test/testcmdlineparser.cpp000066400000000000000000001075001322667425100204520ustar00rootroot00000000000000/* * 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 "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() { 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(debug); 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(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(stdposix); 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 *argv[] = {"cppcheck"}; CmdLineParser parser(&settings); ASSERT(parser.ParseFromArgs(1, argv)); ASSERT_EQUALS(true, parser.GetShowHelp()); } void helpshort() { REDIRECT; const char *argv[] = {"cppcheck", "-h"}; CmdLineParser parser(&settings); ASSERT(parser.ParseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.GetShowHelp()); } void helplong() { REDIRECT; const char *argv[] = {"cppcheck", "--help"}; CmdLineParser parser(&settings); ASSERT(parser.ParseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.GetShowHelp()); } void showversion() { REDIRECT; const char *argv[] = {"cppcheck", "--version"}; CmdLineParser parser(&settings); ASSERT(parser.ParseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.GetShowVersion()); } void onefile() { REDIRECT; const char *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 *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 *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 *argv[] = {"cppcheck", "-v", "file.cpp"}; settings.verbose = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.verbose); } void verboselong() { REDIRECT; const char *argv[] = {"cppcheck", "--verbose", "file.cpp"}; settings.verbose = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.verbose); } void debug() { REDIRECT; const char *argv[] = {"cppcheck", "--debug", "file.cpp"}; settings.debug = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.debug); } void debugwarnings() { REDIRECT; const char *argv[] = {"cppcheck", "--debug-warnings", "file.cpp"}; settings.debugwarnings = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.debugwarnings); } void forceshort() { REDIRECT; const char *argv[] = {"cppcheck", "-f", "file.cpp"}; settings.force = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.force); } void forcelong() { REDIRECT; const char *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 *argvs[] = {"cppcheck", "-rp", "file.cpp"}; ASSERT(defParser.ParseFromArgs(3, argvs)); ASSERT_EQUALS(true, settings.relativePaths); settings.relativePaths = false; const char *argvl[] = {"cppcheck", "--relative-paths", "file.cpp"}; ASSERT(defParser.ParseFromArgs(3, argvl)); ASSERT_EQUALS(true, settings.relativePaths); settings.relativePaths = false; settings.basePaths.clear(); const char *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 *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 *argv[] = {"cppcheck", "-q", "file.cpp"}; settings.quiet = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.quiet); } void quietlong() { REDIRECT; const char *argv[] = {"cppcheck", "--quiet", "file.cpp"}; settings.quiet = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.quiet); } void defines_noarg() { REDIRECT; const char *argv[] = {"cppcheck", "-D"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.ParseFromArgs(2, argv)); } void defines_noarg2() { REDIRECT; const char *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 *argv[] = {"cppcheck", "-D", "--quiet", "file.cpp"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.ParseFromArgs(4, argv)); } void defines() { REDIRECT; const char *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 *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 *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 *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 *argv[] = {"cppcheck", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.ParseFromArgs(2, argv)); ASSERT_EQUALS(Settings::None, settings.enforcedLang); } { const char *argv[] = {"cppcheck", "-x", "c++", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS(Settings::CPP, settings.enforcedLang); } { const char *argv[] = {"cppcheck", "-x"}; ASSERT(!defParser.ParseFromArgs(2, argv)); } { const char *argv[] = {"cppcheck", "-x", "--inconclusive", "file.cpp"}; ASSERT(!defParser.ParseFromArgs(4, argv)); } { const char *argv[] = {"cppcheck", "--language=c++", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(Settings::CPP, settings.enforcedLang); } { const char *argv[] = {"cppcheck", "--language=c", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(Settings::C, settings.enforcedLang); } { const char *argv[] = {"cppcheck", "--language=unknownLanguage", "file.cpp"}; ASSERT(!defParser.ParseFromArgs(3, argv)); } } void includesnopath() { REDIRECT; const char *argv[] = {"cppcheck", "-I"}; // Fails since -I has no param ASSERT_EQUALS(false, defParser.ParseFromArgs(2, argv)); } void includes() { REDIRECT; const char *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 *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 *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 *argv[] = {"cppcheck", "-Iinclude", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includes2() { REDIRECT; const char *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() { // TODO: Fails since cannot open the file REDIRECT; const char *argv[] = {"cppcheck", "--includes-file=inclpaths.txt", "file.cpp"}; settings.includePaths.clear(); ASSERT_EQUALS(true, defParser.ParseFromArgs(3, argv)); } void enabledAll() { REDIRECT; const char *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 *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 *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 *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 *argv[] = {"cppcheck", "--enable=unusedFunction", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::UNUSED_FUNCTION)); } void enabledMissingInclude() { REDIRECT; const char *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 *argv[] = {"cppcheck", "--enable=internal", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::INTERNAL)); } #endif void enabledMultiple() { REDIRECT; const char *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 *argv[] = {"cppcheck", "--inconclusive"}; settings.inconclusive = false; ASSERT(defParser.ParseFromArgs(2, argv)); ASSERT_EQUALS(true, settings.inconclusive); } void errorExitcode() { REDIRECT; const char *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 *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 *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 *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 *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 *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 *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 *argv[] = {"cppcheck", "--file-list=-", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.ParseFromArgs(3, argv)); } */ void inlineSuppr() { REDIRECT; const char *argv[] = {"cppcheck", "--inline-suppr", "file.cpp"}; ASSERT(defParser.ParseFromArgs(3, argv)); } void jobs() { REDIRECT; const char *argv[] = {"cppcheck", "-j", "3", "file.cpp"}; settings.jobs = 0; ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS(3, settings.jobs); } void jobsMissingCount() { REDIRECT; const char *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 *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 *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 *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 *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 *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 *argv[] = {"cppcheck", "--report-progress", "file.cpp"}; settings.reportProgress = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.reportProgress); } void stdposix() { REDIRECT; const char *argv[] = {"cppcheck", "--std=posix", "file.cpp"}; settings.standards.posix = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.standards.posix); } void stdc99() { REDIRECT; const char *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 *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 *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 *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 *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 *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 *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 *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); } } void suppressionSingle() { REDIRECT; const char *argv[] = {"cppcheck", "--suppress=uninitvar", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U)); } void suppressionSingleFile() { REDIRECT; const char *argv[] = {"cppcheck", "--suppress=uninitvar:file.cpp", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U)); } void suppressionTwo() { REDIRECT; const char *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("uninitvar", "file.cpp", 1U)); TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed("noConstructor", "file.cpp", 1U)); } void suppressionTwoSeparate() { REDIRECT; const char *argv[] = {"cppcheck", "--suppress=uninitvar", "--suppress=noConstructor", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed("noConstructor", "file.cpp", 1U)); } void templates() { REDIRECT; const char *argv[] = {"cppcheck", "--template", "{file}:{line},{severity},{id},{message}", "file.cpp"}; settings.outputFormat.clear(); ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS("{file}:{line},{severity},{id},{message}", settings.outputFormat); } void templatesGcc() { REDIRECT; const char *argv[] = {"cppcheck", "--template", "gcc", "file.cpp"}; settings.outputFormat.clear(); ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS("{file}:{line}: {severity}: {message}", settings.outputFormat); } void templatesVs() { REDIRECT; const char *argv[] = {"cppcheck", "--template", "vs", "file.cpp"}; settings.outputFormat.clear(); ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS("{file}({line}): {severity}: {message}", settings.outputFormat); } void templatesEdit() { REDIRECT; const char *argv[] = {"cppcheck", "--template", "edit", "file.cpp"}; settings.outputFormat.clear(); ASSERT(defParser.ParseFromArgs(4, argv)); ASSERT_EQUALS("{file} +{line}: {severity}: {message}", settings.outputFormat); } void xml() { REDIRECT; const char *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 *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 *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 *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 *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 *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 *argv[] = {"cppcheck", "--doc"}; ASSERT(defParser.ParseFromArgs(2, argv)); ASSERT(defParser.ExitAfterPrinting()); } void showtime() { REDIRECT; const char *argv[] = {"cppcheck", "--showtime=summary"}; settings.showtime = SHOWTIME_NONE; ASSERT(defParser.ParseFromArgs(2, argv)); ASSERT(settings.showtime == SHOWTIME_SUMMARY); } void errorlist1() { REDIRECT; const char *argv[] = {"cppcheck", "--errorlist"}; ASSERT(defParser.ParseFromArgs(2, argv)); ASSERT(defParser.GetShowErrorMessages()); } void errorlistverbose1() { REDIRECT; const char *argv[] = {"cppcheck", "--verbose", "--errorlist"}; settings.verbose = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.verbose); } void errorlistverbose2() { REDIRECT; const char *argv[] = {"cppcheck", "--errorlist", "--verbose"}; settings.verbose = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.verbose); } void ignorepathsnopath() { REDIRECT; const char *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 *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 *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 *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 *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 *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 *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 *argv[] = {"cppcheck", "--check-config", "file.cpp"}; settings.checkConfiguration = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.checkConfiguration); } void unknownParam() { REDIRECT; const char *argv[] = {"cppcheck", "--foo", "file.cpp"}; ASSERT(!defParser.ParseFromArgs(3, argv)); } void undefs() { REDIRECT; const char *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 *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 *argv[] = {"cppcheck", "-U"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.ParseFromArgs(2, argv)); } void undefs_noarg2() { REDIRECT; const char *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 *argv[] = {"cppcheck", "-U", "--quiet", "file.cpp"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.ParseFromArgs(4, argv)); } }; REGISTER_TEST(TestCmdlineParser) cppcheck-1.82/test/testcondition.cpp000066400000000000000000002576301322667425100176220ustar00rootroot00000000000000/* * 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 "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() { 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(duplicateIf); // duplicate 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(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(oppositeInnerConditionAnd); TEST_CASE(identicalConditionAfterEarlyExit); 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(checkInvalidTestForOverflow); TEST_CASE(checkConditionIsAlwaysTrueOrFalseInsideIfWhile); TEST_CASE(pointerAdditionResultNotNull); } 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; files.push_back(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); tokenizer.simplifyTokenList2(); checkCondition.runSimplifiedChecks(&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:6]: (style) Condition 'y!=0' is always true\n[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()); } 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); tokenizer.simplifyTokenList2(); checkCondition.runSimplifiedChecks(&tokenizer, &settings1, this); } void duplicateIf() { check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 1) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 2) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a++) { b = 1; }\n" " else { if (a++) { b = 2; }\n" " else { if (a++) { b = 3; } } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int &b) {\n" " if (!strtok(NULL, \" \")) { b = 1; }\n" " else { if (!strtok(NULL, \" \")) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(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()); } 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" "}"); TODO_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" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:3]: (warning) Logical conjunction always evaluates to false: neg < -1.0 && neg > -1.0.\n", 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 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("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" "}"); TODO_ASSERT_EQUALS("error", "", 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()); } 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" "\n" " if (x<4) {\n" " if (x!=5) {}\n" // <- TODO " }\n" "\n" " if (x<4) {\n" " if (x>5) {}\n" // <- Warning " }\n" "\n" " if (x<4) {\n" " if (x>=5) {}\n" // <- Warning " }\n" "\n" " if (x<4) {\n" " if (x<5) {}\n" " }\n" "\n" " if (x<4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n" "[test.cpp:11] -> [test.cpp:12]: (warning) Opposite inner 'if' condition leads to a dead code block.\n" "[test.cpp:15] -> [test.cpp:16]: (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" " }\n" "\n" " if (x<5) {\n" " if (x!=4) {}\n" " }\n" "\n" " if (x<5) {\n" " if (x>4) {}\n" // <- TODO " }\n" "\n" " if (x<5) {\n" " if (x>=4) {}\n" " }\n" "\n" " if (x<5) {\n" " if (x<4) {}\n" " }\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" "\n" " if (x>4) {\n" " if (x>5) {}\n" " }\n" "\n" " if (x>4) {\n" " if (x>=5) {}\n" // <- TODO " }\n" "\n" " if (x>4) {\n" " if (x<5) {}\n" // <- TODO " }\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" "\n" " if (x>5) {\n" " if (x>4) {}\n" // <- TODO " }\n" "\n" " if (x>5) {\n" " if (x>=4) {}\n" // <- TODO " }\n" "\n" " if (x>5) {\n" " if (x<4) {}\n" // <- Warning " }\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" "[test.cpp:15] -> [test.cpp:16]: (warning) Opposite inner 'if' condition leads to a dead code block.\n" "[test.cpp:19] -> [test.cpp:20]: (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 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 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("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" "}"); ASSERT_EQUALS("", 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()); } // 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() {\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() {\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) Condition 'x&3==2' is always false\n" "[test.cpp:2]: (style) Condition '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", 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()); } 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("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("void f() {\n" // #6898 (Token::expressionString) " int x = 0;\n" " A(x++ == 1);\n" " A(x++ == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'x++==1' is always false\n" "[test.cpp:4]: (style) Condition 'x++==2' is always false\n", 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()); // #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 warn about 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("[test.cpp:2]: (style) Condition ''a'' is always true\n" "[test.cpp:3]: (style) Condition 'L'b'' is always true\n" "[test.cpp:4]: (style) Condition '1&&'c'' is always true\n" "[test.cpp:4]: (style) Condition ''c'' is always true\n" "[test.cpp:5]: (style) Condition ''d'' is always true\n", 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 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()); } }; REGISTER_TEST(TestCondition) cppcheck-1.82/test/testconstructors.cpp000066400000000000000000003567051322667425100204070ustar00rootroot00000000000000/* * 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 "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"); tokenizer.simplifyTokenList2(); // Check class constructors.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.constructors(); } void run() { 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(noConstructor10); // ticket #6614 TEST_CASE(noConstructor11); // ticket #3552 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_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_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(uninitVarEnum); 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(uninitVarHeader1); // Class is defined in header TEST_CASE(uninitVarHeader2); // Class is defined in header TEST_CASE(uninitVarHeader3); // Class is defined in header 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(uninitAssignmentWithOperator); // ticket #7429 TEST_CASE(uninitCompoundAssignment); // ticket #7429 TEST_CASE(uninitComparisonAssignment); // ticket #7429 } 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.\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.\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.\n" "[test.cpp:3]: (style) The class 'Wilma' does not have a constructor.\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.\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 noConstructor10() { // ticket #6614 check("class A : public wxDialog\n" "{\n" "private:\n" " DECLARE_EVENT_TABLE()\n" "public:\n" " A(wxWindow *parent,\n" " wxWindowID id = 1,\n" " const wxString &title = wxT(""),\n" " const wxPoint& pos = wxDefaultPosition,\n" " const wxSize& size = wxDefaultSize,\n" " long style = wxDIALOG_NO_PARENT | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX);\n" " virtual ~A();\n" "private:\n" " wxTimer *WxTimer1;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor11() { // #3552 check("class Fred { int x; };\n" "union U { int y; Fred fred; };"); 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_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()); } void initvar_private_constructor() { 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("[test.cpp:11]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n" "[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 assigned a value in 'A::operator='.\n", errout.str()); check("class B\n" "{\n" "public:\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("[test.cpp:12]: (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" "};\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: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()); 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" "};\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" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B (B & Var);\n" " B & operator= (const B & Var);\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" "};"); 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 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("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:4]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); check("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:4]: (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 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 uninitVarEnum() { 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 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() { check("class Foo {\n" " int foo;\n" " Foo() { }\n" "};"); ASSERT_EQUALS("", 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()); } void uninitVarHeader1() { check("#line 1 \"fred.h\"\n" "class Fred\n" "{\n" "private:\n" " unsigned int i;\n" "public:\n" " Fred();\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void uninitVarHeader2() { check("#line 1 \"fred.h\"\n" "class Fred\n" "{\n" "private:\n" " unsigned int i;\n" "public:\n" " Fred() { }\n" "};\n"); ASSERT_EQUALS("[fred.h:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarHeader3() { check("#line 1 \"fred.h\"\n" "class Fred\n" "{\n" "private:\n" " mutable int i;\n" "public:\n" " Fred() { }\n" "};\n"); ASSERT_EQUALS("[fred.h:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", 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 Containter {\n" " Containter();\n" " T* mElements;\n" "};\n" "template Containter::Containter() : mElements(nullptr) {}\n" "Containter intContainer;"); 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()); } }; REGISTER_TEST(TestConstructors) cppcheck-1.82/test/testcppcheck.cpp000066400000000000000000000056671322667425100174150ustar00rootroot00000000000000/* * 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 "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*/) { } void reportErr(const ErrorLogger::ErrorMessage &msg) { id.push_back(msg._id); } }; void run() { 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); } }; REGISTER_TEST(TestCppcheck) cppcheck-1.82/test/testerrorlogger.cpp000066400000000000000000000405761322667425100201640ustar00rootroot00000000000000/* * 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 "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), barCpp8("bar.cpp", 8) { } private: const ErrorLogger::ErrorMessage::FileLocation fooCpp5; const ErrorLogger::ErrorMessage::FileLocation barCpp8; void run() { 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(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, (int)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; locs.push_back(fooCpp5); locs.push_back(barCpp8); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.", "errorId", false); ASSERT_EQUALS(2, (int)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, (int)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; locs.push_back(fooCpp5); locs.push_back(barCpp8); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(2, (int)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, (int)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, (int)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; locs.push_back(fooCpp5); locs.push_back(barCpp8); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(2, (int)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; locs.push_back(fooCpp5); locs.push_back(barCpp8); 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 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.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U)); suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "*", 0U)); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression in a.c errout.str(""); suppressions.clear(); suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U)); suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "a.c", 0U)); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress unmatchedSuppression in a.c at line 10 errout.str(""); suppressions.clear(); suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U)); suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "a.c", 10U)); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // don't suppress unmatchedSuppression when file is mismatching errout.str(""); suppressions.clear(); suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U)); suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "b.c", 0U)); 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.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U)); suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "a.c", 1U)); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); } }; REGISTER_TEST(TestErrorLogger) cppcheck-1.82/test/testexceptionsafety.cpp000066400000000000000000000337501322667425100210410ustar00rootroot00000000000000/* * 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 "checkexceptionsafety.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestExceptionSafety : public TestFixture { public: TestExceptionSafety() : TestFixture("TestExceptionSafety") { } private: Settings settings; void run() { 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.runSimplifiedChecks(&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.82/test/testfilelister.cpp000066400000000000000000000050361322667425100177650ustar00rootroot00000000000000/* * 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 "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() { // 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.82/test/testfiles.pri000066400000000000000000000050501322667425100167310ustar00rootroot00000000000000# no manual edits - this file is autogenerated by dmake INCLUDEPATH += ../externals/tinyxml SOURCES += $${BASEPATH}/test64bit.cpp \ $${BASEPATH}/testassert.cpp \ $${BASEPATH}/testastutils.cpp \ $${BASEPATH}/testautovariables.cpp \ $${BASEPATH}/testbool.cpp \ $${BASEPATH}/testboost.cpp \ $${BASEPATH}/testbufferoverrun.cpp \ $${BASEPATH}/testcharvar.cpp \ $${BASEPATH}/testclass.cpp \ $${BASEPATH}/testcmdlineparser.cpp \ $${BASEPATH}/testcondition.cpp \ $${BASEPATH}/testconstructors.cpp \ $${BASEPATH}/testcppcheck.cpp \ $${BASEPATH}/testerrorlogger.cpp \ $${BASEPATH}/testexceptionsafety.cpp \ $${BASEPATH}/testfilelister.cpp \ $${BASEPATH}/testfunctions.cpp \ $${BASEPATH}/testgarbage.cpp \ $${BASEPATH}/testimportproject.cpp \ $${BASEPATH}/testincompletestatement.cpp \ $${BASEPATH}/testinternal.cpp \ $${BASEPATH}/testio.cpp \ $${BASEPATH}/testleakautovar.cpp \ $${BASEPATH}/testlibrary.cpp \ $${BASEPATH}/testmathlib.cpp \ $${BASEPATH}/testmemleak.cpp \ $${BASEPATH}/testnullpointer.cpp \ $${BASEPATH}/testoptions.cpp \ $${BASEPATH}/testother.cpp \ $${BASEPATH}/testpath.cpp \ $${BASEPATH}/testpathmatch.cpp \ $${BASEPATH}/testpostfixoperator.cpp \ $${BASEPATH}/testpreprocessor.cpp \ $${BASEPATH}/testrunner.cpp \ $${BASEPATH}/testsamples.cpp \ $${BASEPATH}/testsimplifytemplate.cpp \ $${BASEPATH}/testsimplifytokens.cpp \ $${BASEPATH}/testsimplifytypedef.cpp \ $${BASEPATH}/testsizeof.cpp \ $${BASEPATH}/teststl.cpp \ $${BASEPATH}/teststring.cpp \ $${BASEPATH}/testsuite.cpp \ $${BASEPATH}/testsuppressions.cpp \ $${BASEPATH}/testsymboldatabase.cpp \ $${BASEPATH}/testthreadexecutor.cpp \ $${BASEPATH}/testtimer.cpp \ $${BASEPATH}/testtoken.cpp \ $${BASEPATH}/testtokenize.cpp \ $${BASEPATH}/testtokenlist.cpp \ $${BASEPATH}/testtype.cpp \ $${BASEPATH}/testuninitvar.cpp \ $${BASEPATH}/testunusedfunctions.cpp \ $${BASEPATH}/testunusedprivfunc.cpp \ $${BASEPATH}/testunusedvar.cpp \ $${BASEPATH}/testvaarg.cpp \ $${BASEPATH}/testvalueflow.cpp \ $${BASEPATH}/testvarid.cpp cppcheck-1.82/test/testfunctions.cpp000066400000000000000000001256311322667425100176370ustar00rootroot00000000000000/* * 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 "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() { settings.addEnabled("style"); settings.addEnabled("warning"); settings.addEnabled("portability"); settings.standards.posix = true; 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); // 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); // Simplify... tokenizer.simplifyTokenList2(); // Check... checkFunctions.runSimplifiedChecks(&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("int f() { strtol(a,b,sizeof(a)!=12); }"); ASSERT_EQUALS("[test.cpp:1]: (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 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]: (warning) Passing value -1 to sqrt() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1 to sqrtf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1 to sqrtl() leads to implementation-defined result.\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 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" "}"); 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", 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" "}"); 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", 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" "}"); 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", 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" "}"); 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", 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" "}"); 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", 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 << 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 << 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 << log(2.0) << std::endl;\n" " std::cout << logf(2.0) << std::endl;\n" " std::cout << logf(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]: (warning) Passing value 1.1 to acos() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value 1.1 to acosf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value 1.1 to acosl() leads to implementation-defined result.\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]: (warning) Passing value -1.1 to acos() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1.1 to acosf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1.1 to acosl() leads to implementation-defined result.\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]: (warning) Passing value 1.1 to asin() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value 1.1 to asinf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value 1.1 to asinl() leads to implementation-defined result.\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]: (warning) Passing value -1.1 to asin() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1.1 to asinf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1.1 to asinl() leads to implementation-defined result.\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); } 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.82/test/testgarbage.cpp000066400000000000000000001640321322667425100172150ustar00rootroot00000000000000/* * 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 "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() { 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(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(garbageCode14); // #5595 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); 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(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(enumTrailingComma); } 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(); // call all "runSimplifiedChecks" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { (*it)->runSimplifiedChecks(&tokenizer, &settings, this); } return tokenizer.tokens()->stringifyList(false, false, false, true, false, 0, 0); } void wrong_syntax1() { { const char code[] ="TR(kvmpio, PROTO(int rw), ARGS(rw), TP_(aa->rw;))"; ASSERT_EQUALS("TR ( kvmpio , PROTO ( int rw ) , ARGS ( rw ) , TP_ ( aa . rw ; ) )", checkCode(code)); 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("Analysis failed. If the code is valid then please report this failure.", e.errorMessage); ASSERT_EQUALS("cppcheckError", 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); } 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() { checkCode("1 (int j) { return return (c) * sizeof } y[1];"); 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 garbageCode14() { checkCode("static f() { int i; int source[1] = { 1 }; for (i = 0; i < 4; i++) (u, if (y u.x e)) }"); // Garbage code } 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 checkCode("sizeof <= A"); } 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 checkCode("int { }; struct A a = { }"); } 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 checkCode(" ( ( ) ) { } ( { ( __builtin_va_arg_pack ( ) ) ; } ) { ( int { ( ) ( ( ) ) } ( ) { } ( ) ) += ( ) }"); } void garbageCode51() { // #6719 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 = }"); } void garbageCode53() { // #6721 checkCode("{ { } }; void foo (struct int i) { x->b[i] = = }"); } 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 checkCode("{ }> {= ~A()^{} }P { }"); checkCode("{= ~A()^{} }P { } { }> is"); } 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 checkCode("((X (128))) (int a) { v[ = {} (x 42) a] += }"); // 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 checkCode("typedef __attribute__((vector_size (16))) { return[ (v2df){ } ;] }"); // do not crash } 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 checkCode("{ } x x ; { } h h [ ] ( ) ( ) { struct x ( x ) ; int __attribute__ ( ) f ( ) { h - > first = & x ; struct x * n = h - > first ; ( ) n > } }"); // do not crash } void garbageCode96() { // #6807 ASSERT_THROW(checkCode("typedef J J[ ; typedef ( ) ( ) { ; } typedef J J ;] ( ) ( J cx ) { n } ;"), InternalError); } 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 unsiged 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 checkCode("( ) { ( i< ) } int foo ( ) { int i ; ( for ( i => 1 ) ; ) }"); } void garbageCode101() { // #6835 // Reported case checkCode("template < class , =( , int) X = 1 > struct A { } ( ) { = } [ { } ] ( ) { A < void > 0 }"); // Reduced case checkCode("template < class =( , ) X = 1> struct A {}; A a;"); } 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 checkCode("template < Types > struct S {> ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) } { ( )> >} { ( ) { } } ( ) { ( ) }"); } 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 ASSERT_THROW(checkCode("Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" " for (unsigned i = 0 ; i < count; i++) {\n" " }\n" "});"), InternalError); } 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() { 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 ; ( ) ( ) }"); 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() { checkCode("( void ) { ( ) } ( ) / { ( ) }"); // actually the invalid code should trigger an syntax error... } void garbageCode132() { // #7022 checkCode("() () { } { () () ({}) i() } void i(void(*ptr) ()) { ptr(!) () }"); } 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); { errout.str(""); const char code[] = "{\n" " a(\n" "}\n" "{\n" " b());\n" "}\n"; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); try { tokenizer.tokenize(istr, "test.cpp"); assertThrowFail(__FILE__, __LINE__); } catch (InternalError& e) { ASSERT_EQUALS("Invalid number of character '(' when no macros are defined.", e.errorMessage); ASSERT_EQUALS("syntaxError", e.id); ASSERT_EQUALS(2, e.token->linenr()); } } } void garbageCode134() { // Ticket #5605, #5759, #5762, #5774, #5823, #6059 ASSERT_THROW(checkCode("foo() template struct tuple Args> tuple { } main() { foo(); }"), InternalError); checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"); checkCode("() template < T = typename = x > struct a {} { f () }"); checkCode("template < T = typename = > struct a { f }"); 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 checkCode("\" \" typedef signed char f; \" \"; void a() { f * s = () &[]; (; ) (; ) }"); } 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 checkCode("({}typedef typename x;typename x!){({{}()})}"); // don't hang } void garbageCode160() { // #7190 ASSERT_THROW(checkCode("f(a,b,c,d)float [ a[],d;int ] b[],c;{} "), InternalError); // don't hang } 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"; checkCode(code); // #6106 code = " f { int i ; b2 , [ ] ( for ( i = 0 ; ; ) ) }"; checkCode(code); // 6122 survive garbage code code = "; { int i ; for ( i = 0 ; = 123 ; ) - ; }"; checkCode(code); code = "void f1() { for (int n = 0 n < 10 n++); }"; checkCode(code); } void garbageSymbolDatabase() { checkCode("void f( { u = 1 ; } ) { }"); checkCode("{ }; void namespace A::f; { g() { int } }"); 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) } 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 checkCode("d i(){{f*s=typeid(()0,)}}", false); } 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 checkCode("{r e() { w*constD = (())D = cast< }}"); } 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() { checkCode("int test() { int +; }"); } // #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); } void garbageCode188() { // #8255 ASSERT_THROW(checkCode("{z r(){(){for(;<(x);){if(0==0)}}}}"), InternalError); } void garbageCode189() { // #8317 checkCode("t&n(){()()[](){()}}$"); } void garbageCode190() { // #8307 checkCode("void foo() {\n" " int i;\n" " i *= 0;\n" " !i <;\n" "}"); } void syntaxErrorFirstToken() { ASSERT_THROW(checkCode("&operator(){[]};"), InternalError); // #7818 ASSERT_THROW(checkCode("*(*const<> (size_t); foo) { } *(*const (size_t)() ; foo) { }"), InternalError); // #6858 ASSERT_THROW(checkCode(">{ x while (y) z int = }"), InternalError); // #4175 ASSERT_THROW(checkCode("&p(!{}e x){({(0?:?){({})}()})}"), InternalError); // #7118 ASSERT_THROW(checkCode(" { struct { typename D4:typename Base }; };"), InternalError); // #3533 ASSERT_THROW(checkCode(" > template < . > struct Y < T > { = } ;\n"), InternalError); // #6108 } void syntaxErrorLastToken() { ASSERT_THROW(checkCode("int *"), InternalError); // #7821 ASSERT_THROW(checkCode("x[y]"), InternalError); // #2986 ASSERT_THROW(checkCode("( ) &"), InternalError); ASSERT_THROW(checkCode("|| #if #define <="), InternalError); // #2601 ASSERT_THROW(checkCode("f::y:y : \n"), InternalError); ASSERT_THROW(checkCode("++4++ + + E++++++++++ + ch " "tp.oed5[.]"), InternalError); // #7074 ASSERT_THROW(checkCode("d a(){f s=0()8[]s?():0}*()?:0", false), InternalError); // #7236 ASSERT_THROW(checkCode("!2 : #h2 ?:", false), InternalError); // #7769 ASSERT_THROW(checkCode("--"), InternalError); ASSERT_THROW(checkCode("volatile true , test < test < #ifdef __ppc__ true ,"), InternalError); // #4169 ASSERT_THROW(checkCode("a,b--\n"), InternalError); // #2847 ASSERT_THROW(checkCode("x a[0] ="), InternalError); // #2682 ASSERT_THROW(checkCode("auto_ptr\n"), InternalError); // #2967 ASSERT_THROW(checkCode("char a[1]\n"), InternalError); // #2865 ASSERT_THROW(checkCode("<><<"), InternalError); // #2612 ASSERT_THROW(checkCode("z"), InternalError); // #2831 ASSERT_THROW(checkCode("><,f. */ #include "importproject.h" #include "testsuite.h" #include #include #include class TestImporter : public ImportProject { public: void importCompileCommands(std::istream &istr) { ImportProject::importCompileCommands(istr); } }; class TestImportProject : public TestFixture { public: TestImportProject() : TestFixture("TestImportProject") { } private: void run() { TEST_CASE(setDefines); TEST_CASE(setIncludePaths1); TEST_CASE(setIncludePaths2); TEST_CASE(setIncludePaths3); // macro names are case insensitive TEST_CASE(importCompileCommands); } 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; in.push_back("../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; in.push_back("$(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; in.push_back("$(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 importCompileCommands() const { const char json[] = "[ { \"directory\": \"/tmp\"," "\"command\": \"gcc -I/tmp -DTEST1 -DTEST2=2 -DTEST3=\\\"\\\\\\\"3\\\\\\\"\\\" -o /tmp/src.o -c /tmp/src.c\"," "\"file\": \"/tmp/src.c\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("TEST1=1;TEST2=2;TEST3=\"\\\"3\\\"\"", importer.fileSettings.begin()->defines); } }; REGISTER_TEST(TestImportProject) cppcheck-1.82/test/testincompletestatement.cpp000066400000000000000000000177701322667425100217170ustar00rootroot00000000000000/* * 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 "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[]) { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector files; files.push_back("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(""); tokenizer.simplifyTokenList2(); // Check for incomplete statements.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkIncompleteStatement(); } void run() { settings.addEnabled("warning"); TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); TEST_CASE(test4); TEST_CASE(test5); TEST_CASE(test6); 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(block); // ({ do_something(); 0; }) TEST_CASE(mapindex); } 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 test_numeric() { check("struct P\n" "{\n" "double a;\n" "double b;\n" "};\n" "void f()\n" "{\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 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()); } }; REGISTER_TEST(TestIncompleteStatement) cppcheck-1.82/test/testinternal.cpp000066400000000000000000000461701322667425100174430ustar00rootroot00000000000000/* * 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 . */ #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() { 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"); tokenizer.simplifyTokenList2(); // Check.. CheckInternal checkInternal; checkInternal.runSimplifiedChecks(&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()); // 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()); // 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.82/test/testio.cpp000066400000000000000000011707511322667425100162420ustar00rootroot00000000000000/* * 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 "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() { 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); } 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 *"); // TODO TEST_SCANF_WARN("%jd", "intmax_t", "size_t"); // TODO TEST_SCANF_WARN("%jd", "intmax_t", "unsigned ptrdiff_t"); 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 *"); //TODO 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 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is '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 '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 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is '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 'size_t {aka unsigned long long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is '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 '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 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is '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 '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 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is '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 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is '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 '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 '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 '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()); } 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()); } 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" " 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" " 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", false, 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()); } 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()); } }; REGISTER_TEST(TestIO) cppcheck-1.82/test/testleakautovar.cpp000066400000000000000000001317211322667425100201420ustar00rootroot00000000000000/* * 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 "checkleakautovar.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestLeakAutoVar : public TestFixture { public: TestLeakAutoVar() : TestFixture("TestLeakAutoVar") { } private: Settings settings; void run() { int id = 0; while (!settings.library.ismemory(++id)); settings.library.setalloc("malloc", id, -1); settings.library.setdealloc("free", id, 1); while (!settings.library.isresource(++id)); settings.library.setalloc("fopen", id, -1); settings.library.setdealloc("fclose", id, 1); // Assign TEST_CASE(assign1); TEST_CASE(assign2); TEST_CASE(assign3); TEST_CASE(assign4); TEST_CASE(assign5); TEST_CASE(assign6); TEST_CASE(assign7); TEST_CASE(assign8); TEST_CASE(assign9); TEST_CASE(assign10); TEST_CASE(assign11); // #3942: x = a(b(p)); TEST_CASE(assign12); // #4236: FP. bar(&x); TEST_CASE(assign13); // #4237: FP. char*&ref=p; p=malloc(10); free(ref); TEST_CASE(assign14); TEST_CASE(deallocuse1); TEST_CASE(deallocuse2); TEST_CASE(deallocuse3); TEST_CASE(deallocuse4); TEST_CASE(deallocuse5); // #4018: FP. free(p), p = 0; TEST_CASE(deallocuse6); // #4034: FP. x = p = f(); TEST_CASE(deallocuse7); // #6467, #6469, #6473 TEST_CASE(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); // 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)) // switch TEST_CASE(switch1); // loops TEST_CASE(loop1); // mismatching allocation/deallocation TEST_CASE(mismatchAllocDealloc); // Execution reaches a 'return' TEST_CASE(return1); TEST_CASE(return2); TEST_CASE(return3); TEST_CASE(return4); TEST_CASE(return5); // 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 } 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"); tokenizer.simplifyTokenList2(); // Check for leaks.. CheckLeakAutoVar c; settings.checkLibrary = true; settings.addEnabled("information"); c.runSimplifiedChecks(&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 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: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()); } 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 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: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: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: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: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: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: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: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: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: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: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: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: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: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: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 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 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: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: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: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()); } 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 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() { check("void f() {\n" " char *p = malloc(10);\n" " if (set_data(p)) { }\n" "}"); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " if (set_data(p)) { return; }\n" "}"); 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()); } 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()); } }; 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"); tokenizer.simplifyTokenList2(); // Check for leaks.. CheckLeakAutoVar checkLeak; checkLeak.runSimplifiedChecks(&tokenizer, &settings, this); } void run() { 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.82/test/testlibrary.cpp000066400000000000000000000715771322667425100173040ustar00rootroot00000000000000/* * 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 "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 class TestLibrary : public TestFixture { public: TestLibrary() : TestFixture("TestLibrary") { } private: Settings settings; void run() { 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_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); } static Library::Error readLibrary(Library& library, const char* xmldata) { tinyxml2::XMLDocument doc; doc.Parse(xmldata); return library.load(doc); } void empty() const { const char xmldata[] = "\n"; Library library; readLibrary(library, xmldata); 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; readLibrary(library, xmldata); 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; readLibrary(library, xmldata); { 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; readLibrary(library, xmldata); ASSERT(library.isNotLibraryFunction(tokenList.front())); } void function_match_args_default() const { const char xmldata[] = "\n" "\n" " \n" " " " " " \n" ""; Library library; readLibrary(library, xmldata); { 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; readLibrary(library, xmldata); ASSERT(library.isNotLibraryFunction(tokenList.front()->next())); } void function_arg() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; Library library; readLibrary(library, xmldata); ASSERT_EQUALS(true, 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; readLibrary(library, xmldata); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[-1].notuninit); } void function_arg_variadic() const { const char xmldata[] = "\n" "\n" "\n" " \n" " \n" "\n" ""; Library library; readLibrary(library, xmldata); ASSERT_EQUALS(true, 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_valid() const { const char xmldata[] = "\n" "\n" " \n" " 1:\n" " -7:0\n" " 1:5,8\n" " -1,5\n" " :1,5\n" " \n" ""; Library library; readLibrary(library, xmldata); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d,e);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); // 1- ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, -10)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, 0)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 1)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 10)); // -7-0 ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, -10)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -7)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -3)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, 0)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, 1)); // 1-5,8 ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 0)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 1)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 3)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 5)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 6)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 7)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 8)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 9)); // -1,5 ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, -10)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 4, -1)); // :1,5 ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, -10)); ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, 1)); ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 5, 2)); } void function_arg_minsize() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" ""; Library library; readLibrary(library, xmldata); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c);"); 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); } } void function_namespace() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; Library library; readLibrary(library, xmldata); 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; readLibrary(library, xmldata); 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; readLibrary(library, xmldata); { 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; readLibrary(library, xmldata); 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; readLibrary(library, xmldata); ASSERT(library.functions.empty()); ASSERT(Library::ismemory(library.alloc("CreateX"))); ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); const Library::AllocFunc* af = library.alloc("CreateX"); ASSERT(af && af->arg == -1); const Library::AllocFunc* df = library.dealloc("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; readLibrary(library, xmldata); ASSERT(library.functions.empty()); const Library::AllocFunc* af = library.alloc("CreateX"); ASSERT(af && af->arg == 5); const Library::AllocFunc* df = library.dealloc("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; readLibrary(library, xmldata); 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" ""; Library library; readLibrary(library, xmldata); const struct Library::PodType *type = library.podtype("s16"); ASSERT_EQUALS(2U, type ? type->size : 0U); ASSERT_EQUALS((char)0, type ? type->sign : '?'); } 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; readLibrary(library, xmldata); 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_EQUALS(Library::Container::SIZE, A.getYield("size")); ASSERT_EQUALS(Library::Container::EMPTY, A.getYield("empty")); ASSERT_EQUALS(Library::Container::AT_INDEX, A.getYield("at")); ASSERT_EQUALS(Library::Container::START_ITERATOR, A.getYield("begin")); ASSERT_EQUALS(Library::Container::END_ITERATOR, A.getYield("end")); ASSERT_EQUALS(Library::Container::BUFFER, A.getYield("data")); ASSERT_EQUALS(Library::Container::BUFFER_NT, A.getYield("c_str")); ASSERT_EQUALS(Library::Container::ITEM, A.getYield("front")); ASSERT_EQUALS(Library::Container::NO_YIELD, A.getYield("foo")); ASSERT_EQUALS(Library::Container::RESIZE, A.getAction("resize")); ASSERT_EQUALS(Library::Container::CLEAR, A.getAction("clear")); ASSERT_EQUALS(Library::Container::PUSH, A.getAction("push_back")); ASSERT_EQUALS(Library::Container::POP, A.getAction("pop_back")); ASSERT_EQUALS(Library::Container::FIND, A.getAction("find")); ASSERT_EQUALS(Library::Container::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; Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(err.errorcode, Library::OK); } { const char xmldata [] = "\n" "\n" ""; Library library; Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(err.errorcode, Library::OK); } { const char xmldata [] = "\n" "\n" ""; Library library; Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(err.errorcode, Library::UNSUPPORTED_FORMAT); } } }; REGISTER_TEST(TestLibrary) cppcheck-1.82/test/testmathlib.cpp000066400000000000000000001651011322667425100172430ustar00rootroot00000000000000/* * 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 "mathlib.h" #include "testsuite.h" struct InternalError; class TestMathLib : public TestFixture { public: TestMathLib() : TestFixture("TestMathLib") { } private: void run() { 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); // throw ASSERT_THROW(MathLib::divide("-9223372036854775808", "-1"), InternalError); // #4520 - out of range => throw ASSERT_EQUALS("4611686018427387904", MathLib::divide("-9223372036854775808", "-2")); // #6679 MathLib::divide("123", "0.0"); // don't throw // 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'")); #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); // 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(false, MathLib::isFloatHex("")); 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(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")); } 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("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(void) const { // negative testing std::string value = "ux"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "ulx"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "lx"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "lux"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "lll"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "garbage"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value.clear(); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "llu "; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "i"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "iX"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "i6X"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "i64X"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "i64 "; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "i66"; ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix(value.begin(), value.end())); // positive testing value = "u"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "ul"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "ull"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "l"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "lu"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "ll"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "llu"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "i64"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); value = "ui64"; ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix(value.begin(), value.end())); } 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(".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(void) 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")); } 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 { 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 { 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 intepreted 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.82/test/testmemleak.cpp000066400000000000000000006232671322667425100172520ustar00rootroot00000000000000/* * 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 "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() { 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"); 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 ="); CheckMemoryLeak check(&tokenizer, 0, &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[], bool c = false, bool posix = false, bool experimental = false, Settings *settings = nullptr) { // Clear the error buffer.. errout.str(""); if (!settings) settings = &settings1; settings->experimental = experimental; settings->standards.posix = posix; // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, c?"test.c":"test.cpp"); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, settings, this); checkMemoryLeak.checkReallocUsage(); checkMemoryLeak.check(); } void run() { LOAD_LIB_2(settings1.library, "std.cfg"); LOAD_LIB_2(settings1.library, "posix.cfg"); LOAD_LIB_2(settings2.library, "std.cfg"); // Check that getcode works correctly.. TEST_CASE(testgetcode); // check that call_func works correctly.. TEST_CASE(call_func); // Check that simplifycode works correctly.. TEST_CASE(simplifycode); // Check that errors are found.. TEST_CASE(findleak); TEST_CASE(simple5); TEST_CASE(simple7); TEST_CASE(simple9); // Bug 2435468 - member function "free" TEST_CASE(simple11); TEST_CASE(nonstd_free); TEST_CASE(new_nothrow); TEST_CASE(staticvar); TEST_CASE(externvar); TEST_CASE(referencevar); // 3954 - false positive for reference pointer TEST_CASE(alloc_alloc_1); TEST_CASE(ifelse6); TEST_CASE(ifelse7); TEST_CASE(ifelse8); TEST_CASE(ifelse10); TEST_CASE(if4); TEST_CASE(if7); // Bug 2401436 TEST_CASE(if8); // Bug 2458532 TEST_CASE(if9); // if (realloc) TEST_CASE(if10); // else if (realloc) TEST_CASE(if11); TEST_CASE(if12); // Ticket #7745 TEST_CASE(forwhile5); TEST_CASE(forwhile6); TEST_CASE(forwhile8); // Bug 2429936 TEST_CASE(forwhile9); TEST_CASE(forwhile10); TEST_CASE(forwhile11); TEST_CASE(forwhile12); TEST_CASE(switch2); TEST_CASE(switch3); TEST_CASE(ret5); // Bug 2458436 - return use TEST_CASE(ret6); TEST_CASE(ret7); TEST_CASE(ret8); TEST_CASE(mismatch1); TEST_CASE(mismatch2); TEST_CASE(mismatch3); TEST_CASE(mismatch4); TEST_CASE(mismatch5); TEST_CASE(mismatch6); TEST_CASE(mismatchSize); TEST_CASE(func3); TEST_CASE(func4); TEST_CASE(func5); TEST_CASE(func6); TEST_CASE(func7); TEST_CASE(func9); // Embedding the function call in a if-condition TEST_CASE(func10); // Bug 2458510 - Function pointer TEST_CASE(func11); // Bug 2458510 - Function pointer TEST_CASE(func12); TEST_CASE(func13); TEST_CASE(func14); TEST_CASE(func15); TEST_CASE(func16); TEST_CASE(func17); TEST_CASE(func18); TEST_CASE(func19); // Ticket #2056 - if (!f(p)) return 0; TEST_CASE(func20); // Ticket #2182 - exit is not handled TEST_CASE(func21); // Ticket #2569 TEST_CASE(func22); // Ticket #2668 TEST_CASE(func23); // Ticket #2667 TEST_CASE(func24); // Ticket #2705 TEST_CASE(func25); // Ticket #2904 TEST_CASE(func26); TEST_CASE(func27); // Ticket #2773 TEST_CASE(func28); // Ticket #3236 TEST_CASE(allocfunc1); TEST_CASE(allocfunc2); TEST_CASE(allocfunc3); TEST_CASE(allocfunc4); TEST_CASE(allocfunc5); TEST_CASE(allocfunc6); TEST_CASE(allocfunc7); TEST_CASE(allocfunc8); TEST_CASE(allocfunc9); TEST_CASE(allocfunc10); TEST_CASE(allocfunc11); TEST_CASE(allocfunc12); // #3660: allocating and returning non-local pointer => not allocfunc TEST_CASE(allocfunc13); // Ticket #4494 and #4540 - class function TEST_CASE(allocfunc14); // Use pointer before returning it TEST_CASE(inlineFunction); // #3989 - inline function TEST_CASE(throw1); TEST_CASE(throw2); TEST_CASE(linux_list_1); TEST_CASE(linux_list_2); TEST_CASE(sizeof1); TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); TEST_CASE(realloc4); TEST_CASE(realloc5); TEST_CASE(realloc6); 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(assign1); TEST_CASE(varid); TEST_CASE(cast1); // Using deallocated memory: // * It is ok to take the address to deallocated memory // * It is not ok to dereference a pointer to deallocated memory TEST_CASE(dealloc_use); TEST_CASE(dealloc_use_2); TEST_CASE(dealloc_use_3); TEST_CASE(dealloc_use_4); // #7960 // free a free'd pointer TEST_CASE(freefree1); TEST_CASE(freefree2); TEST_CASE(freefree3); // #4236 - FP. bar(&p) TEST_CASE(strcpy_result_assignment); TEST_CASE(strcat_result_assignment); TEST_CASE(all1); // Extra checking when --all is given TEST_CASE(malloc_constant_1); // Check that the malloc constant matches the type // Calls to unknown functions.. they may throw exception, quit the program, etc TEST_CASE(unknownFunction1); TEST_CASE(unknownFunction2); TEST_CASE(unknownFunction4); TEST_CASE(unknownFunction5); // detect leak in class member function.. TEST_CASE(class1); TEST_CASE(class2); TEST_CASE(autoptr1); TEST_CASE(if_with_and); TEST_CASE(assign_pclose); TEST_CASE(conditional_dealloc_return); // #7820 // Using the function "exit" TEST_CASE(exit2); TEST_CASE(exit4); TEST_CASE(exit5); TEST_CASE(exit6); TEST_CASE(exit7); TEST_CASE(noreturn); TEST_CASE(strndup_function); TEST_CASE(tmpfile_function); TEST_CASE(fcloseall_function); TEST_CASE(file_functions); TEST_CASE(getc_function); TEST_CASE(open_function); TEST_CASE(creat_function); TEST_CASE(close_function); TEST_CASE(fd_functions); TEST_CASE(pointer_to_pointer); TEST_CASE(dealloc_and_alloc_in_func); // Unknown syntax TEST_CASE(unknownSyntax1); TEST_CASE(knownFunctions); TEST_CASE(same_function_name); // #1440 - Check function parameters also.. TEST_CASE(functionParameter); // setjmp/longjmp.. TEST_CASE(jmp); TEST_CASE(trac1949); TEST_CASE(trac2540); // #2662: segfault because of endless recursion (call_func -> getAllocationType -> functionReturnType -> call_func ..) TEST_CASE(trac2662); // #1879 non regression test case TEST_CASE(trac1879); TEST_CASE(ptrptr); TEST_CASE(c_code); TEST_CASE(gnucfg); TEST_CASE(trac3991); TEST_CASE(crash); TEST_CASE(trac7680); TEST_CASE(trac7440); } std::string getcode(const char code[], const char varname[], bool classfunc=false) { // Clear the error buffer.. errout.str(""); settings2.standards.posix = true; // Tokenize.. Tokenizer tokenizer(&settings2, this); std::istringstream istr(code); if (!tokenizer.tokenize(istr, "test.cpp")) return ""; tokenizer.simplifyTokenList2(); const Token * start = tokenizer.tokens(); const SymbolDatabase * db = tokenizer.getSymbolDatabase(); if (db && db->functionScopes.size()) start = db->functionScopes[0]->classStart->next(); const unsigned int varId(Token::findmatch(start, varname)->varId()); // getcode.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings2, nullptr); std::list callstack; callstack.push_back(0); CheckMemoryLeak::AllocType allocType, deallocType; allocType = deallocType = CheckMemoryLeak::No; Token *tokens = checkMemoryLeak.getcode(start, callstack, varId, allocType, deallocType, classfunc, 1); // stringify.. std::ostringstream ret; for (const Token *tok = tokens; tok; tok = tok->next()) ret << tok->str(); TokenList::deleteTokens(tokens); return ret.str(); } void testgetcode() { // alloc; ASSERT_EQUALS(";;alloc;", getcode("int *a = malloc(100);", "a")); TODO_ASSERT_EQUALS(";;alloc;", ";;alloccallfunc;", getcode("int *a = ::malloc(100);", "a")); ASSERT_EQUALS(";;alloc;", getcode("int *a = new int;", "a")); ASSERT_EQUALS(";;alloc;", getcode("int *a = new int[10];", "a")); ASSERT_EQUALS(";;alloc;", getcode("int **a = new int*[10];", "a")); ASSERT_EQUALS(";;alloc;", getcode("int * const a = new int[10];", "a")); ASSERT_EQUALS(";;alloc;", getcode("const int * const a = new int[10];", "a")); ASSERT_EQUALS(";;assign;", getcode("A * a = new (X) A;", "a")); ASSERT_EQUALS(";;alloc;", getcode("int i = open(a,b);", "i")); ASSERT_EQUALS(";;assign;", getcode("int i = open();", "i")); ASSERT_EQUALS(";;alloc;use;", getcode("int *p; dostuff(p = new int);", "p")); ASSERT_EQUALS(";;alloc;use;", getcode("int *p; dostuff(p = new int());", "p")); ASSERT_EQUALS(";;alloc;use;", getcode("int *p; fred.dostuff(p = new int);", "p")); ASSERT_EQUALS(";;alloc;use;", getcode("int *p; fred.dostuff(p = new int());", "p")); // alloc; return use; ASSERT_EQUALS(";;alloc;returnuse;", getcode("int *a = new int[10]; return a;", "a")); ASSERT_EQUALS(";;alloc;returnuse;", getcode("char *a = new char[100]; return (char *)a;", "a")); // alloc; return; ASSERT_EQUALS(";;alloc;return;", getcode("char *s = new char[100]; return 0;", "s")); ASSERT_EQUALS(";;alloc;return;", getcode("char *s = new char[100]; return s[0];", "s")); ASSERT_EQUALS(";;alloc;return;", getcode("char *s = new char[100]; return strcmp(s,x);", "s")); // lock/unlock.. ASSERT_EQUALS(";;alloc;", getcode("int a; __cppcheck_lock();", "")); ASSERT_EQUALS(";;callfunc;", getcode("int a; __cppcheck_lock();", "a")); ASSERT_EQUALS(";;dealloc;", getcode("int a; __cppcheck_unlock();", "")); ASSERT_EQUALS(";;callfunc;", getcode("int a; __cppcheck_unlock();", "a")); // dealloc; ASSERT_EQUALS(";;dealloc;", getcode("char *s; free(s);", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; free((void *)s);", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; free((void *)(s));", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; free(reinterpret_cast(s));", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; ::free(s);", "s")); // #2802 ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete s;", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete (s);", "s")); TODO_ASSERT_EQUALS(";;dealloc;", ";;;", getcode("char *s; delete (void *)(s);", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete [] s;", "s")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete [] (s);", "s")); ASSERT_EQUALS(";;dealloc;", getcode("void *p; foo(fclose(p));", "p")); ASSERT_EQUALS(";;dealloc;", getcode("void *p; foo(close(p));", "p")); ASSERT_EQUALS(";;;;", getcode("FILE *f1; FILE *f2; fclose(f1);", "f2")); ASSERT_EQUALS(";;returnuse;", getcode("FILE *f; return fclose(f) == EOF ? 1 : 2;", "f")); ASSERT_EQUALS(";;dealloc;", getcode("char *s; s ? free(s) : 0;", "s")); // if.. ASSERT_EQUALS(";;if{}", getcode("char *s; if (a) { }", "s")); ASSERT_EQUALS(";;dealloc;ifv{}", getcode("FILE *f; if (fclose(f)) { }", "f")); ASSERT_EQUALS(";;if(!var){}else{}", getcode("char *s; if (!s) { } else { }", "s")); TODO_ASSERT_EQUALS(";;ifv{}",";;if{}", getcode("char *s; if (a && s) { }", "s")); ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (s && a) { }", "s")); ASSERT_EQUALS(";;;ifv{}", getcode("char *s; int a; if (a && s) { }", "s")); ASSERT_EQUALS(";;;ifv{}", getcode("char *s; int a; if (s && a) { }", "s")); ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (a || s) { }", "s")); ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (s || a) { }", "s")); ASSERT_EQUALS(";;if(!var){}", getcode("char *s; if (a && !s) { }", "s")); ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (foo(!s)) { }", "s")); ASSERT_EQUALS(";;;if{dealloc;};if{dealloc;return;}assign;returnuse;", getcode("char *buf, *tmp; tmp = realloc(buf, 40); if (!(tmp)) { free(buf); return; } buf = tmp; return buf;", "buf")); ASSERT_EQUALS(";;if{}", getcode("FILE *f; if (fgets(buf,100,f)){}", "f")); ASSERT_EQUALS(";;alloc;if(var){dealloc;}", getcode("int fd = open(a,b); if (0 < fd) { close(fd); }", "fd")); ASSERT_EQUALS(";;use;if{}", getcode("char *s; if (x(s)) { }", "s")); ASSERT_EQUALS(";;use;if{}", getcode("char *s; if (x(&s)) { }", "s")); ASSERT_EQUALS(";;use;if{}", getcode("char *s; if (!s || x(&s)) { }", "s")); ASSERT_EQUALS(";;ifv{}", getcode("int ffd; if (ffd<0 && (ffd=a)<0){}", "ffd")); // if (ticket #2442) ASSERT_EQUALS(";;;;if(!var){;}ifv{}", getcode("char *s; int x = 0; if (!s) { x = 2; } if (x) { }", "s")); ASSERT_EQUALS(";;;;if(!var){;}if{}", getcode("char *s; int x = 0; if (!s) { x = 2; } if (y) { }", "s")); // switch.. ASSERT_EQUALS(";;switch{case;;break;};", getcode("char *s; switch(a){case 1: break;};", "s")); // loop.. ASSERT_EQUALS(";;loop{}", getcode("char *s; while (a) { }", "s")); ASSERT_EQUALS(";;loopcallfunc{}", getcode("char *s; while (a()) { }", "s")); ASSERT_EQUALS(";;loop{}", getcode("char *s; for (a;b;c) { }", "s")); ASSERT_EQUALS(";;loop{alloc;}", getcode("char *s; for (a;b;c) { s=malloc(10); }", "s")); ASSERT_EQUALS(";;do{}loop;", getcode("char *s; do { } while (a);", "s")); ASSERT_EQUALS(";;while1{}", getcode("char *s; while(true) { }", "s")); ASSERT_EQUALS(";;while1{}", getcode("char *s; for(;;) { }", "s")); ASSERT_EQUALS(";;while(var){}", getcode("char *s; while (s) { }", "s")); ASSERT_EQUALS(";;while(!var){}", getcode("char *s; while (!s) { }", "s")); ASSERT_EQUALS(";;alloc;while(var){}", getcode("int fd = open(a,b); while (fd >= 0) { }", "fd")); ASSERT_EQUALS(";;alloc;while(!var){}", getcode("int fd = open(a,b); while (fd < 0) { }", "fd")); // asprintf.. ASSERT_EQUALS(";;alloc;", getcode("char *s; asprintf(&s, \"xyz\");", "s")); ASSERT_EQUALS(";;alloc;", getcode("char *s; asprintf(&s, \"s: %s\", s);", "s")); ASSERT_EQUALS(";;;", getcode("char *s; asprintf(&p, \"s: %s\", s);", "s")); // Since we don't check how the return value is used we must bail out ASSERT_EQUALS("", getcode("char *s; int ret = asprintf(&s, \"xyz\");", "s")); TODO_ASSERT_EQUALS(";;alloc;", "", getcode("char *s; int ret; ret=asprintf(&s, \"xyz\"); if (ret==-1) return;", "s")); // use.. ASSERT_EQUALS(";;use;", getcode("char *s; a(s);", "s")); ASSERT_EQUALS(";;use;", getcode("char *s; (*a)(s);", "s")); ASSERT_EQUALS(";;use;", getcode("char *s; abc.a(s);", "s")); ASSERT_EQUALS(";;use;", getcode("char *s; s2 = s;", "s")); ASSERT_EQUALS(";;use;", getcode("char *s; s2 = s + 10;", "s")); ASSERT_EQUALS(";;use;", getcode("char *s; s2 = x + s;", "s")); ASSERT_EQUALS(";;use;if{;}", getcode("char *s; if (foo(s)) ;", "s")); ASSERT_EQUALS(";;use;", getcode("char *s; map1[s] = 0;", "s")); ASSERT_EQUALS(";;;use;", getcode("char *p; const char *q; q = p;", "p")); ASSERT_EQUALS(";;use;;", getcode("char *s; x = {1,s};", "s")); ASSERT_EQUALS(";{};;alloc;;use;", getcode("struct Foo { }; Foo *p; p = malloc(10); const Foo *q; q = p;", "p")); ASSERT_EQUALS(";;useuse_;", getcode("struct AB *ab; f(ab->a);", "ab")); ASSERT_EQUALS(";;use;", getcode("struct AB *ab; ab = pop(ab);", "ab")); // non-use.. ASSERT_EQUALS(";;use_;", getcode("char *s; c = x + s[0];","s")); ASSERT_EQUALS(";;use_;", getcode("char *s; c = s[0] + x;","s")); ASSERT_EQUALS(";;use_;", getcode("type *c; y = x + c->y;","c")); ASSERT_EQUALS(";;use_;", getcode("type *c; y = c->y + x;","c")); ASSERT_EQUALS(";;use_;", getcode("char *s; s = s + 1;", "s")); ASSERT_EQUALS(";;dealloc;;", getcode("struct foo *s; free(s); printf(a,sizeof(*s));", "s")); ASSERT_EQUALS(";;do{dealloc;;}while(var);", getcode("struct foo *s; do{free(s); printf(a,sizeof(*s));}while(s);", "s")); // use reference ASSERT_EQUALS(";;callfunc&use;", getcode("struct AB *ab; f(&ab);", "ab")); // return.. ASSERT_EQUALS(";;return;", getcode("char *s; return;", "s")); ASSERT_EQUALS(";;returnuse;", getcode("char *s; return s;", "s")); ASSERT_EQUALS(";;return;", getcode("char *s; return 5 + s[0];", "s")); // assign.. ASSERT_EQUALS(";;assign;", getcode("char *s; s = 0;", "s")); ASSERT_EQUALS(";;;", getcode("char *s; s = strcpy(s, p);", "s")); // callfunc.. ASSERT_EQUALS(";;assign;", getcode("char *s; s = a();", "s")); ASSERT_EQUALS(";;callfunc;", getcode("char *s; a();", "s")); ASSERT_EQUALS(";;callfunc;", getcode("char *s; abc.a();", "s")); ASSERT_EQUALS(";;;", getcode("char *s; x = a();", "s")); // the function call is irrelevant // exit.. ASSERT_EQUALS(";;exit;", getcode("char *s; exit(0);", "s")); ASSERT_EQUALS(";;callfunc;", getcode("char *s; _exit(0);", "s")); // not in std.cfg ASSERT_EQUALS(";;exit;", getcode("char *s; abort();", "s")); ASSERT_EQUALS(";;callfunc;", getcode("char *s; err(0);", "s")); // not in std.cfg ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { exit(0); }", "s")); ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { ::exit(0); }", "s")); ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { std::exit(0); }", "s")); // list_for_each ASSERT_EQUALS(";;exit;{}}", getcode("void f() { char *s; list_for_each(x,y,s) { } }", "s")); // open/close ASSERT_EQUALS(";;alloc;if(var){dealloc;}", getcode("int f; f=open(a,b); if(f>=0)close(f);", "f")); ASSERT_EQUALS(";;alloc;if(var){dealloc;}", getcode("int f; f=open(a,b); if(f>-1)close(f);", "f")); ASSERT_EQUALS(";;alloc;ifv{;}", getcode("int f; f=open(a,b); if(f!=-1 || x);", "f")); ASSERT_EQUALS(";;;dealloc;loop{}", getcode(";int f; while (close(f) == -1) { }", "f")); ASSERT_EQUALS(";;;dealloc;assign;;", getcode(";int res; res = close(res);", "res")); ASSERT_EQUALS(";;dealloc;", getcode("int f; e |= fclose(f);", "f")); ASSERT_EQUALS(";;dealloc;", getcode("int f; e += fclose(f);", "f")); ASSERT_EQUALS(";;dealloc;", getcode("int f; foo(fclose(f));", "f")); // fcloseall.. ASSERT_EQUALS(";;alloc;;", getcode("char *s; s = malloc(10); fcloseall();", "s")); ASSERT_EQUALS(";;alloc;dealloc;", getcode("FILE *f; f = fopen(a,b); fcloseall();", "f")); // call memcpy in class function.. ASSERT_EQUALS(";;alloc;;", getcode("char *s; s = new char[10]; memcpy(s,a);", "s", true)); // #2112 - Segmentation fault in the getcode function ASSERT_THROW(getcode("page *one = foo();\n" "ASSERT(one, return 0)\n" "const int two = rand();\n" "return 0;\n" "}", "one"), InternalError); // ticket #2336: calling member function with same name as a white_list function ASSERT_EQUALS(";;use;", getcode("char *s; foo.write(s);", "s")); // #2473 - inner struct ASSERT_EQUALS(";;alloc;{;;};dealloc;", getcode("char *s = new char[10];\n" "struct ab { int a, b; };\n" "delete [] s;\n", "s")); // #4405 - catch ASSERT_EQUALS(";;catch{}", getcode("char *s; catch(err) { }", "s")); } bool test_white_list(const std::string& str, bool cpp = true) const { return CheckMemoryLeakInFunction::test_white_list(str, &settings1, cpp); } void call_func() const { // whitelist.. ASSERT_EQUALS(true, test_white_list("qsort")); ASSERT_EQUALS(true, test_white_list("scanf")); ASSERT_EQUALS(true, test_white_list("sscanf")); // #1293 ASSERT_EQUALS(true, test_white_list("time")); ASSERT_EQUALS(true, test_white_list("asctime")); ASSERT_EQUALS(true, test_white_list("asctime_r")); ASSERT_EQUALS(true, test_white_list("ctime")); ASSERT_EQUALS(true, test_white_list("ctime_r")); ASSERT_EQUALS(true, test_white_list("gmtime")); ASSERT_EQUALS(true, test_white_list("gmtime_r")); ASSERT_EQUALS(true, test_white_list("localtime")); ASSERT_EQUALS(true, test_white_list("localtime_r")); ASSERT_EQUALS(true, test_white_list("memcmp")); ASSERT_EQUALS(true, test_white_list("gets")); ASSERT_EQUALS(true, test_white_list("vprintf")); ASSERT_EQUALS(true, test_white_list("vfprintf")); ASSERT_EQUALS(true, test_white_list("vsprintf")); ASSERT_EQUALS(true, test_white_list("snprintf")); ASSERT_EQUALS(true, test_white_list("vsnprintf")); ASSERT_EQUALS(true, test_white_list("delete", true)); ASSERT_EQUALS(false, test_white_list("delete", false)); static const char * const call_func_white_list[] = { "access", "asprintf", "atof", "atoi", "atol", "chdir", "chmod", "clearerr", "chown" , "fchmod", "fcntl", "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets" , "flock", "for", "fprintf", "fputc", "fputs", "fread", "free", "freopen", "fscanf", "fseek" , "fseeko", "fsetpos", "fstat", "fsync", "ftell", "ftello", "ftruncate" , "fwrite", "getc", "if", "ioctl", "lockf", "lseek", "open", "memchr", "memcpy" , "memmove", "memset", "mkstemp", "perror", "posix_fadvise", "posix_fallocate", "pread" , "printf", "puts", "pwrite", "read", "readahead", "readdir", "readdir_r", "readv" , "realloc", "return", "rewind", "rewinddir", "scandir", "seekdir" , "setbuf", "setbuffer", "setlinebuf", "setvbuf", "snprintf", "sprintf", "stpcpy", "strcasecmp" , "strcat", "strchr", "strcmp", "strcpy", "stricmp", "strlen", "strncat", "strncmp" , "strncpy", "strrchr", "strspn","strstr", "strtod", "strtol", "strtoul", "switch" , "sync_file_range", "telldir", "typeid", "while", "write", "writev", "lstat", "stat" , "_open", "_wopen", "vscanf", "vsscanf", "vfscanf", "vasprintf", "utime", "utimes", "unlink" , "tempnam", "system", "symlink", "strpbrk", "strncasecmp", "strdup", "strcspn", "strcoll" , "setlocale", "sethostname", "rmdir", "rindex", "rename", "remove", "adjtime", "creat", "execle" , "execl", "execlp", "execve", "execv", "fmemopen", "fnmatch", "fopencookie", "fopen" , "getgrnam", "gethostbyaddr", "getnetbyname", "getopt", "getopt_long", "getprotobyname", "getpwnam" , "getservbyname", "getservbyport", "glob", "index", "inet_addr", "inet_aton", "inet_network" , "initgroups", "link", "mblen", "mbstowcs", "mbtowc", "mkdir", "mkfifo", "mknod", "obstack_printf" , "obstack_vprintf", "opendir", "parse_printf_format", "pathconf", "popen", "psignal" , "readlink", "regcomp", "strxfrm", "wordexp", "sizeof", "strtok" }; for (unsigned int i = 0; i < (sizeof(call_func_white_list) / sizeof(char *)); ++i) { bool ret = test_white_list(call_func_white_list[i]); ASSERT_EQUALS("", ret ? "" : call_func_white_list[i]); } } std::string simplifycode(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. std::istringstream istr(code); Tokenizer tokenizer(&settings0, this); tokenizer.list.createTokens(istr, "test.cpp"); // replace "if ( ! var )" => "if(!var)" for (Token *tok = tokenizer.list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "if|while ( var )")) { Token::eraseTokens(tok, tok->tokAt(4)); tok->str(tok->str() + "(var)"); } else if (Token::Match(tok, "if|while ( ! var )")) { Token::eraseTokens(tok, tok->tokAt(5)); tok->str(tok->str() + "(!var)"); } } CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings0, this); checkMemoryLeak.simplifycode(tokenizer.list.front()); return tokenizer.tokens()->stringifyList(0, false); } // Test that the CheckMemoryLeaksInFunction::simplifycode works void simplifycode() { ASSERT_EQUALS(";", simplifycode("; ; ; ;")); ASSERT_EQUALS(";", simplifycode("; if ;")); ASSERT_EQUALS("alloc ;", simplifycode("alloc ; if ; if(var) ; ifv ; if(!var) ;")); ASSERT_EQUALS("alloc ;", simplifycode("alloc ; if ; else ;")); // use.. ASSERT_EQUALS("; use ; }", simplifycode("; use use ; }")); ASSERT_EQUALS("; use ; }", simplifycode("; use use_ ; }")); ASSERT_EQUALS("; use ; }", simplifycode("; use_ use ; }")); ASSERT_EQUALS("; use ; }", simplifycode("; &use use ; }")); ASSERT_EQUALS("; use ; }", simplifycode("; use &use ; }")); ASSERT_EQUALS("; alloc ; dealloc ; }", simplifycode("; alloc ; use ; use ; if use ; dealloc ; }")); // if, else.. ASSERT_EQUALS("; alloc ; if break ; dealloc ;", simplifycode("; alloc ; if { break; } dealloc ;")); ASSERT_EQUALS("; alloc ; if continue ; dealloc ;", simplifycode("; alloc ; if { continue; } dealloc ;")); ASSERT_EQUALS("; alloc ;", simplifycode("; alloc; if { return use; }")); ASSERT_EQUALS("; alloc ; dealloc ;", simplifycode("; alloc; if(!var) { return; } dealloc;")); ASSERT_EQUALS("; alloc ;", simplifycode("; if { alloc; } else { return; }")); ASSERT_EQUALS("; alloc ; dealloc ;", simplifycode("; alloc ; if(!var) { alloc ; } dealloc ;")); ASSERT_EQUALS("; use ;", simplifycode("; if(var) use ;")); ASSERT_EQUALS("; break ;", simplifycode("; if break ; else break ;")); ASSERT_EQUALS("; alloc ; if return ;", simplifycode("; alloc ; loop { if return ; if continue ; }")); ASSERT_EQUALS("; alloc ; if return ;", simplifycode("; alloc ; loop { if continue ; else return ; }")); ASSERT_EQUALS("; alloc ; if dealloc ;", simplifycode("; alloc ; if(!var) { return ; } if { dealloc ; }")); ASSERT_EQUALS("; if alloc ; else assign ; return use ;", simplifycode("; callfunc ; if callfunc { alloc ; } else { assign ; } return use ;")); ASSERT_EQUALS("; dealloc ; return ;", simplifycode("; while1 { if callfunc { dealloc ; return ; } else { continue ; } }")); // remove outer if (#2733) ASSERT_EQUALS("alloc ; return ; }", simplifycode("alloc ; if { if return use ; } return ; }")); ASSERT_EQUALS("alloc ; return ; }", simplifycode("alloc ; if { if(var) return use ; } return ; }")); ASSERT_EQUALS("alloc ; return ; }", simplifycode("alloc ; if(var) { if return use ; } return ; }")); // "if ; .." ASSERT_EQUALS("; if xxx ;", simplifycode("; if ; else xxx ;")); ASSERT_EQUALS("; if(var) xxx ;", simplifycode("; if(!var) ; else xxx ;")); ASSERT_EQUALS("; if(!var) xxx ;", simplifycode("; if(var) ; else xxx ;")); ASSERT_EQUALS("; ifv xxx ;", simplifycode("; ifv ; else xxx ;")); ASSERT_EQUALS("; alloc ;", simplifycode("; alloc; if { dealloc; return; }")); ASSERT_EQUALS("; alloc ;", simplifycode("; alloc; if { return use; }")); ASSERT_EQUALS("; alloc ; return ;", simplifycode(";alloc;if{return;}return;")); ASSERT_EQUALS("; alloc ; if assign ; dealloc ;", simplifycode(";alloc;if{assign;}dealloc;")); // if(var) ASSERT_EQUALS("; alloc ; return use ;", simplifycode("; alloc ; return use ;")); ASSERT_EQUALS("; alloc ; return use ;", simplifycode("; alloc ; ifv return ; return use ;")); // switch.. ASSERT_EQUALS("; alloc ; dealloc ;", simplifycode(";alloc;switch{case;break;};dealloc;")); ASSERT_EQUALS(";", simplifycode("; switch { case ; return ; default ; break ; }")); ASSERT_EQUALS(";", simplifycode("; switch { case ; if { return ; } break ; default ; break ; }")); ASSERT_EQUALS("; use ;", simplifycode("; switch { case ; return ; default ; use ; break ; }")); ASSERT_EQUALS("; use ;", simplifycode("; while1 { loop { ; } switch { case ; dealloc ; return ; default ; break ; } }")); ASSERT_EQUALS("; { dealloc ; return ; } }", simplifycode("switch { case ; case ; dealloc ; return ; default ; dealloc ; return ; } }")); // loops.. ASSERT_EQUALS(";", simplifycode("; loop { ; }")); ASSERT_EQUALS(";", simplifycode("; loop { break; }")); ASSERT_EQUALS(";", simplifycode("; loop { if { break; } }")); ASSERT_EQUALS("; loop alloc ;", simplifycode("; loop { alloc ; }")); ASSERT_EQUALS("; alloc ; alloc ;", simplifycode("; alloc ; do { alloc ; } loop ;")); ASSERT_EQUALS("; exit ;", simplifycode("; alloc ; do { } loop ; exit ;")); ASSERT_EQUALS("; loop use ;", simplifycode("; loop { loop loop use ; } ;")); ASSERT_EQUALS("; }", simplifycode("; loop { if break ; break ; } ; }")); ASSERT_EQUALS("; }", simplifycode("; loop { if continue ; if continue ; } ; }")); { // ticket #3267 const char expected[] = "; loop if alloc ; if { dealloc ; return ; } }"; ASSERT_EQUALS(expected, simplifycode("; loop { if alloc ; } if { dealloc ; return ; } }")); ASSERT_EQUALS(expected, simplifycode("; loop { if { alloc ; if(!var) { return ; } } } if { dealloc ; return ; } }")); } ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; while(!var) alloc ;")); ASSERT_EQUALS("; alloc ; dealloc ; return ;", simplifycode("; alloc ; while1 { if { dealloc ; return ; } }")); ASSERT_EQUALS("; alloc ; dealloc ; return ;", simplifycode("; alloc ; while1 { if { dealloc ; return ; } if { continue ; } }")); ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; while1 { if { dealloc ; return ; } if { break ; } }")); ASSERT_EQUALS("; alloc ; use ; }", simplifycode("; alloc ; while1 { if { dealloc ; return ; } continue ; } ; }")); ASSERT_EQUALS(";", simplifycode("; do { dealloc ; alloc ; } while(var) ;")); ASSERT_EQUALS("dealloc ; alloc ;", simplifycode("loop { dealloc ; alloc ; }")); ASSERT_EQUALS("dealloc ; alloc ;", simplifycode("while1 { dealloc ; alloc ; }")); ASSERT_EQUALS("use ; }", simplifycode("loop { use ; callfunc ; } }")); ASSERT_EQUALS(";", simplifycode("; loop { if { continue ; } else { if continue ; } }")); ASSERT_EQUALS(";", simplifycode("; loop { { if continue ; if continue ; } }")); ASSERT_EQUALS("; use ;", simplifycode("; while1 { if { dealloc ; return ; } if { if { continue ; } } }")); // scope.. TODO_ASSERT_EQUALS("; assign ; if alloc ; }", "; assign ; dealloc ; if alloc ; }", simplifycode("; assign ; { dealloc ; if alloc ; } }")); // callfunc.. ASSERT_EQUALS("; callfunc ; }", simplifycode(";callfunc;}")); ASSERT_EQUALS(";", simplifycode(";callfunc;;")); ASSERT_EQUALS("; callfunc ; }", simplifycode(";callfunc callfunc ; }")); ASSERT_EQUALS("dealloc ; alloc ; return ; }", simplifycode("while1 { dealloc ; alloc ; } callfunc ; return ; }")); ASSERT_EQUALS("; }", simplifycode("loop callfunc ; }")); ASSERT_EQUALS("alloc ; dealloc ; }", simplifycode("alloc ; if { dealloc ; callfunc } dealloc ; }")); // #4405 // #2900 - don't report false positive ASSERT_EQUALS("; alloc ; if { if { dealloc ; callfunc ; } return ; } dealloc ; }", simplifycode("; alloc ; if { if { dealloc ; callfunc ; } return ; } dealloc ; }")); // exit.. ASSERT_EQUALS("; exit ;", simplifycode("; alloc; exit;")); ASSERT_EQUALS("; exit ;", simplifycode("; alloc; if { loop ; } dealloc; exit;")); ASSERT_EQUALS(";", simplifycode("; if { alloc; exit; }")); ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; if { use; exit; }")); ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; if(!var) { exit; }")); TODO_ASSERT_EQUALS(";", "; if(var) exit ;", simplifycode("; alloc ; if(var) { exit; }")); TODO_ASSERT_EQUALS(";\n; alloc ;", "; alloc ; ifv exit ;", simplifycode("; alloc ; ifv { exit; }")); // try-catch ASSERT_EQUALS("; }", simplifycode("; try ; catch exit ; }")); // dealloc; dealloc; ASSERT_EQUALS("; alloc ; if dealloc ; dealloc ;", simplifycode("; alloc ; if { dealloc ; } dealloc ;")); // use ; dealloc ; ASSERT_EQUALS("; alloc ; use ; if return ; dealloc ;", simplifycode("; alloc ; use ; if { return ; } dealloc ;")); // #2635 - false negative ASSERT_EQUALS("; alloc ; return use ; }", simplifycode("; alloc ; if(!var) { loop { ifv { } } alloc ; } return use; }")); } // is there a leak in given code? if so, return the linenr unsigned int dofindleak(const char code[]) { // Clear the error buffer.. errout.str(""); settings0.debug = settings0.debugwarnings = true; // Tokenize.. std::istringstream istr(code); TokenList list(&settings0); list.createTokens(istr,"test.cpp"); Token *tokens=list.front(); // replace "if ( ! var )" => "if(!var)" for (Token *tok = tokens; tok; tok = tok->next()) { if (tok->str() == "if_var") { tok->str("if(var)"); } else if (Token::simpleMatch(tok, "if ( var )")) { Token::eraseTokens(tok, tok->tokAt(4)); tok->str("if(var)"); } else if (Token::simpleMatch(tok, "if ( ! var )")) { Token::eraseTokens(tok, tok->tokAt(5)); tok->str("if(!var)"); } } const Token *tok = CheckMemoryLeakInFunction::findleak(tokens); settings0.debug = settings0.debugwarnings = false; return (tok ? tok->linenr() : (unsigned int)(-1)); } void findleak() { static const unsigned int notfound = (unsigned int)(-1); ASSERT_EQUALS(1, dofindleak("alloc;")); ASSERT_EQUALS(1, dofindleak("; use; { alloc; }")); ASSERT_EQUALS(2, dofindleak("alloc;\n return;")); ASSERT_EQUALS(notfound, dofindleak("alloc; return use;")); ASSERT_EQUALS(2, dofindleak("alloc;\n callfunc;")); ASSERT_EQUALS(notfound, dofindleak("alloc; use;")); ASSERT_EQUALS(notfound, dofindleak("assign; alloc; dealloc;")); ASSERT_EQUALS(notfound, dofindleak("assign; if alloc; dealloc;")); // if alloc.. ASSERT_EQUALS(2, dofindleak("if alloc;\n return;")); ASSERT_EQUALS(notfound, dofindleak("if alloc;\n return use;")); ASSERT_EQUALS(notfound, dofindleak("if alloc;\n use;")); ASSERT_EQUALS(notfound, dofindleak("if alloc;\n if assign;\n if dealloc; }")); // if.. ASSERT_EQUALS(notfound, dofindleak("alloc; ifv dealloc;")); ASSERT_EQUALS(2, dofindleak("alloc;\n if return;\n dealloc;")); ASSERT_EQUALS(2, dofindleak("alloc;\n if continue;\n dealloc;")); ASSERT_EQUALS(2, dofindleak("alloc;\n if_var return;\n dealloc;")); ASSERT_EQUALS(3, dofindleak("alloc;\n if\n return;\n dealloc;")); ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc ; return; } dealloc;")); ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc ; return; } dealloc;")); ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc ; alloc; } dealloc;")); ASSERT_EQUALS(notfound, dofindleak("alloc;\n if(!var)\n { callfunc;\n return;\n }\n use;")); ASSERT_EQUALS(notfound, dofindleak("alloc; if { return use; } dealloc;")); ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc; return; } dealloc;")); ASSERT_EQUALS(5, dofindleak("{\n;\n alloc;\n if dealloc;\n}")); // assign.. ASSERT_EQUALS(2, dofindleak("alloc;\n assign;\n dealloc;")); ASSERT_EQUALS(notfound, dofindleak("alloc;\n if(!var) assign;\n dealloc;")); ASSERT_EQUALS(2, dofindleak(";alloc;\n if assign;\n dealloc;")); // loop.. TODO_ASSERT_EQUALS(1, notfound, dofindleak("; loop { alloc ; if break; dealloc ; }")); TODO_ASSERT_EQUALS(1, notfound, dofindleak("; loop { alloc ; if continue; dealloc ; }")); ASSERT_EQUALS(notfound, dofindleak("; loop { alloc ; if break; } dealloc ;")); ASSERT_EQUALS(1, dofindleak("; loop alloc ;")); ASSERT_EQUALS(1, dofindleak("; loop alloc ; dealloc ;")); // callfunc (might be noreturn) ASSERT_EQUALS(notfound, dofindleak("; alloc ; callfunc ; }")); } void simple5() { check("static char *f()\n" "{\n" " struct *str = new strlist;\n" " return &str->s;\n" "}"); ASSERT_EQUALS("", errout.str()); } void simple7() { // A garbage collector may delete f automatically check("class Fred;\n" "void foo()\n" "{\n" " Fred *f = new Fred;\n" "}"); ASSERT_EQUALS("", errout.str()); } void simple9() { check("void foo()\n" "{\n" " MyClass *c = new MyClass();\n" " c->free(c);\n" " delete c;\n" "}"); ASSERT_EQUALS("", errout.str()); } void simple11() { check("void Fred::aaa()\n" "{ }\n" "\n" "void Fred::foo()\n" "{\n" " char *s = NULL;\n" " if (a)\n" " s = malloc(10);\n" " else if (b)\n" " s = malloc(10);\n" " else\n" " f();\n" " g(s);\n" " if (c)\n" " h(s);\n" " free(s);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nonstd_free() { check("void f() {\n" " void* mem = malloc(100, foo);" // Non-standard malloc() implementation " free(mem, bar);" // Non-standard free() implementation (#5665) "}"); ASSERT_EQUALS("", errout.str()); } void new_nothrow() { check("void f()\n" "{\n" " int *p = new(std::nothrow) int;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: p\n", errout.str()); check("void f()\n" "{\n" " using std::nothrow;\n" " int *p = new(nothrow) int;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); check("void f()\n" "{\n" " int *p = new(std::nothrow) int[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: p\n", errout.str()); check("void f()\n" "{\n" " using namespace std;\n" " int *p = new(nothrow) int[10];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); check("void f()\n" "{\n" " int *p = new(std::nothrow) int;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: p\n", errout.str()); check("void f()\n" "{\n" " int *p = new(std::nothrow) int[10];\n" " delete p;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: p\n", errout.str()); check("void f()\n" "{\n" " Fred *f = new(nothrow) Fred;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " Fred *f = new(std::nothrow) Fred;\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #2971 check("void f()\n" "{\n" " Fred *f = new(std::nothrow) Fred[10];\n" " delete f;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f()\n" "{\n" " struct Fred *f = new(std::nothrow) struct Fred[10];\n" " delete f;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: f\n", errout.str()); } void staticvar() { check("int f()\n" "{\n" " static char *s = 0;\n" " free(s);\n" " s = malloc(100);\n" " return 123;\n" "}"); ASSERT_EQUALS("", errout.str()); } void externvar() { check("void f()\n" "{\n" " extern char *s;\n" " s = malloc(100);\n" "}"); ASSERT_EQUALS("", errout.str()); } void referencevar() { // 3954 - false positive for reference pointer check("void f() {\n" " char *&x = get();\n" " x = malloc(100);\n" "}"); ASSERT_EQUALS("", errout.str()); } void alloc_alloc_1() { check("void foo()\n" "{\n" " char *str;\n" " str = new char[10];\n" " str = new char[20];\n" " delete [] str;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: str\n", errout.str()); } void ifelse6() { check("static char *f()\n" "{\n" " char *s = new char[100];\n" " if ( a == b )\n" " {\n" " return s;\n" " }\n" " return NULL;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: s\n", errout.str()); } void ifelse7() { check("static char *f()\n" "{\n" " char *s;\n" " if ( abc )\n" " {\n" " s = new char[10];\n" " }\n" " return s;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse8() { check("static char *f()\n" "{\n" " char *s = new char[10];\n" " if ( s )\n" " {\n" " return s;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse10() { check("static char *f()\n" "{\n" " char *s = new char[10];\n" " if ( ghfgf )\n" " {\n" " str[0] = s;\n" " }\n" " else\n" " {\n" " str[0] = s;\n" " }\n" "}\n", false, false, true); ASSERT_EQUALS("", errout.str()); } void if4() { check("void f()\n" "{\n" " char *s;\n" " bool b = true;\n" " if (b && (s = malloc(256)))\n" " ;\n" " if (b)\n" " free(s);\n" "}"); ASSERT_EQUALS("", errout.str()); } void if7() { check("void f( bool b )\n" "{\n" " int *a=0;\n" " if( b )\n" " {\n" " a = new int[10];\n" " }\n" "\n" " if( b )\n" " delete [] a;\n" " else {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void if8() { check("static void f(int i)\n" "{\n" " char *c = malloc(50);\n" " if (i == 1)\n" " {\n" " free(c);\n" " return;\n" " }\n" " if (i == 2)\n" " {\n" " return;\n" " }\n" " free(c);\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: c\n", errout.str()); } void if9() { check("static void f()\n" "{\n" " char *buf = NULL, *tmp;\n" " if (!(tmp = realloc(buf, 50)))\n" " {\n" " free(buf);\n" " return NULL;\n" " }\n" " buf = tmp;\n" " return buf;\n" "}"); ASSERT_EQUALS("", errout.str()); } void if10() { check("static void f()\n" "{\n" " char *buf = malloc(10);\n" " if (aa)\n" " ;\n" " else if (buf = realloc(buf, 100))\n" " ;\n" " free(buf);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Common realloc mistake: \'buf\' nulled but not freed upon failure\n", errout.str()); } void if11() { check("void foo()\n" "{\n" " int *x = new int[10];\n" " if (x == 0 || aa)\n" " {\n" " return 1;\n" " }\n" " delete [] x;\n" "}", false, false, true); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: x\n", "", errout.str()); } void if12() { // #7745 check("void f() {\n" " FILE *fp = fopen(\"name\", \"r\");\n" " if (!fp) {\n" " fp = fopen(\"name\", \"w\");\n" " fclose(fp);\n" " }\n" "}", /*c=*/true, /*posix=*/false); ASSERT_EQUALS("[test.c:7]: (error) Resource leak: fp\n", errout.str()); } void forwhile5() { check("void f(const char **a)\n" "{\n" " char *str = 0;\n" " for (int i = 0; i < 10 && !str; ++i)\n" " {\n" " str = strdup(a[i]);\n" " }\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); } void forwhile6() { check("void f(const char **a)\n" "{\n" " char *str = 0;\n" " for (int i = 0; i < 10 && !str; ++i)\n" " {\n" " str = strdup(a[i]);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: str\n", errout.str()); } void forwhile8() { check("char *f()\n" "{\n" " char *a = 0;\n" " int i = 0;\n" " for( ;; )\n" " {\n" " ++i;\n" " a = realloc( a, i );\n" " if( !a )\n" " return 0;\n" "\n" " if( i > 10 )\n" " break;\n" " }\n" "\n" " return a;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: a\n", "[test.cpp:8]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void forwhile9() { check("char *f()\n" "{\n" " char *a = 0;\n" " int i = 0;\n" " for(i = 0 ;i < 50 ; i++)\n" " {\n" " if(func1(i))\n" " continue;\n" " a = realloc( a, i );\n" " if(func2(i))\n" " continue;\n" " }\n" "\n" " return a;\n" "}\n", false, false, true); ASSERT_EQUALS("[test.cpp:9]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void forwhile10() { check("char *f()\n" "{\n" " char *a = 0;\n" " int i = 0;\n" " for(i = 0; i < 50; i++)\n" " {\n" " if(func1(i))\n" " continue;\n" " a = realloc( a, i );\n" " if(func2(i))\n" " return;\n" " }\n" "\n" " return a;\n" "}", false, false, true); ASSERT_EQUALS("[test.cpp:9]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n" "[test.cpp:11]: (error) Memory leak: a\n", errout.str()); } void forwhile11() { check("int main()\n" "{\n" " FILE *stream=NULL;\n" " while((stream = fopen(name,\"r\")) == NULL)\n" " { }\n" " if(stream!=NULL) fclose(stream);\n" "}"); ASSERT_EQUALS("", errout.str()); } void forwhile12() { check("extern int bar();\n" "void f() {\n" " FILE *fp = fopen(\"name\", \"r\" );\n" " while(bar()) {\n" " fp = fopen(\"name\", \"w\");\n" " fclose(fp);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Resource leak: fp\n", errout.str()); check("void f() {\n" " FILE *fp = fopen(\"name\", \"r\" );\n" " while(1) {\n" " fp = fopen(\"name\", \"w\");\n" " fclose(fp);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fp\n", errout.str()); check("void f() {\n" " FILE *fp = fopen(\"name\", \"r\" );\n" " for( ; ; ) {\n" " fp = fopen(\"name\", \"w\");\n" " fclose(fp);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fp\n", errout.str()); } void switch2() { check("void f()\n" "{\n" " char *str = new char[10];\n" " switch (abc)\n" " {\n" " case 1:\n" " delete [] str;\n" " break;\n" " default:\n" " break;\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (error) Memory leak: str\n", errout.str()); } void switch3() { check("void f()\n" "{\n" " char *str = new char[10];\n" " while (abc)\n" " {\n" " switch (def)\n" " {\n" " default:\n" " return;\n" " }\n" " }\n" " delete [] str;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Memory leak: str\n", errout.str()); } void ret5() { check("static char * f()\n" "{\n" " char *c = new char[50];\n" " return (c ? c : NULL);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ret6() { check("void foo()\n" "{\n" " char *c = new char[50];\n" " return strcpy(c, \"foo\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void ret7() { check("void foo()\n" "{\n" " char *c = new char[50];\n" " return memcpy(c, \"foo\",4);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ret8() { check("char *foo()\n" "{\n" " char *c = new char[50];\n" " return ((char *)(c+1));\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatch1() { check("void f()\n" "{\n" " int *a = new int[10];\n" " free(a);\n" "}\n", false, false, true); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: a\n", errout.str()); // ticket #2971 check("void f()\n" "{\n" " Fred *a = new Fred[10];\n" " free(a);\n" "}\n", false, false, true); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: a\n", errout.str()); check("void f()\n" "{\n" " struct Fred *a = new struct Fred[10];\n" " free(a);\n" "}\n", false, false, true); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: a\n", errout.str()); check("void f() {\n" " char* str = new char[42]();\n" " delete[] str;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatch2() { check("void f()\n" "{\n" " FILE *fp;\n" "\n" " fp = fopen();\n" " fclose(fp);\n" "\n" " fp = popen();\n" " pclose(fp);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void mismatch3() { check("void f()\n" "{\n" " FILE *fp;\n" "\n" " if (abc) fp = fopen();\n" " else fp = popen();\n" "\n" " if (abc) fclose(fp);\n" " else pclose(fp);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void mismatch4() { check("void f()\n" "{\n" " char *p = 0;\n" " for (i = 0; i < 10; ++i)\n" " {\n" " delete p;\n" " p = new char[100];\n" " }\n" " delete [] p;\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Mismatching allocation and deallocation: p\n", errout.str()); } void mismatch5() { check("void f() {\n" " C *c = new C;\n" " delete c;\n" " c = new C[2];\n" " delete [] c;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void mismatch6() { // #4599 check("void test ( int do_gzip , const char * fn ) {\n" " FILE * f ;\n" " if ( do_gzip ) {\n" " f = popen ( fn , \"wb\" ) ;\n" " } else {\n" " f = fopen ( fn , \"wb\" ) ;\n" " }\n" " if ( do_gzip ) {\n" " pclose ( f ) ; }\n" " else {\n" " fclose ( f ) ; }\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatchSize() { check("void f(char *buf)\n" "{\n" " int i;\n" " buf = malloc(3);\n" " buf[i] = 0;\n" " free(buf);\n" "}"); ASSERT_EQUALS("", errout.str()); } //////////////////////////////////////////////// // function calls //////////////////////////////////////////////// void func3() { check("static void foo(const char *str)\n" "{ }\n" "\n" "static void f()\n" "{\n" " char *p = new char[100];\n" " foo(p);\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: p\n", errout.str()); } void func4() { check("static void foo(char *str)\n" "{\n" " delete [] str;\n" "}\n" "\n" "static void f()\n" "{\n" " char *p = new char[100];\n" " foo(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func5() { check("static void foo(char *str)\n" "{\n" " delete str;\n" "}\n" "\n" "static void f()\n" "{\n" " char *p = new char[100];\n" " foo(p);\n" "}", false, false, true); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: str\n", errout.str()); } void func6() { check("static void foo(char *str)\n" "{\n" " goto abc;\n" "}\n" "\n" "static void f()\n" "{\n" " char *p = new char[100];\n" " foo(p);\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: p\n", errout.str()); } void func7() { check("static void foo(char *str)\n" "{\n" " if (abc)\n" " return;" " delete [] str;\n" "}\n" "\n" "static void f()\n" "{\n" " char *p = new char[100];\n" " foo(p);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: p\n", "", errout.str()); } void func9() { check("int b()\n" "{\n" " return 0;\n" "}\n" "\n" "void a()\n" "{\n" " char *a = new char[10];\n" " if (b())\n" " return;\n" " delete [] a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void func10() { check("static void f(void (*fnc)(char*))\n" "{\n" " char *c = malloc(50);\n" " (fnc)(c);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func11() { check("static void f(struct1 *s1)\n" "{\n" " char *c = malloc(50);\n" " (s1->fnc)(c);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func12() { check("void add_list(struct mmtimer *n)\n" "{\n" " rb_link_node(&n->list, parent, link);\n" "}\n" "\n" "int foo()\n" "{\n" " struct mmtimer *base;\n" "\n" " base = kmalloc(sizeof(struct mmtimer), GFP_KERNEL);\n" " if (base == NULL)\n" " return -ENOMEM;\n" "\n" " add_list(base);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func13() { check("static void f()\n" "{\n" " char *p = malloc(100);\n" " foo(&p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func14() { // It is not known what the "foo" that only takes one parameter does.. check("static void foo(char *a, char *b)\n" "{\n" " free(a);\n" " free(b);\n" "}\n" "static void f()\n" "{\n" " char *p = malloc(100);\n" " foo(p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func15() { check("static void a()\n" "{ return true; }\n" "\n" "static void b()\n" "{\n" " char *p = malloc(100);\n" " if (a()) return;\n" // <- memory leak " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: p\n", errout.str()); } void func16() { check("static void a( bo_t *p_bo)\n" "{\n" " p_bo->buffer = realloc( p_bo->buffer, 100 );\n" "}\n" "\n" "static bo_t * b()\n" "{\n" " bo_t *box;\n" " if( ( box = malloc( sizeof( bo_t ) ) ) )\n" " {\n" " a(box);\n" " a(box);\n" " }\n" " return box;\n" "}"); ASSERT_EQUALS("", errout.str()); } void func17() { // The "bar" function must be reduced to "use" check("bool bar(char **parent, char *res, bool a)\n" "{\n" " if( a )\n" " {\n" " *parent = res;\n" " return false;\n" " }\n" " return true;\n" "}\n" "\n" "void foo(char **parent, bool a)\n" "{\n" " if (a)\n" " {\n" " char *res = malloc(65);\n" " bar(parent, res, a);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void func18() { // No false positive // The "free_pointers" will deallocate all pointers check("static void free_pointers(int arg_count, ...)\n" "{\n" " va_list a;\n" " va_start(a, arg_count);\n" " for (int i = 0; i < arg_count; i++)\n" " {\n" " free(va_arg(a, void *));\n" " }\n" " va_end(a);\n" "}\n" "\n" "static char* foo()\n" "{\n" " return strdup(\"\");\n" "}\n" "\n" "static void bar()\n" "{\n" " int *p = malloc(16);\n" " free_pointers(1, p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func19() { // Ticket #2056 check("bool a(int *p) {\n" " return p;\n" "}\n" "\n" "void b() {\n" " int *p = malloc(16);\n" " if (!a(p)) return;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func20() { // Ticket #2182 - false positive when there is unused class. // If the unused class is removed the false positive goes away. // [test.cpp:12]: (error) Deallocating a deallocated pointer: p check("class test {\n" " void f();\n" "};\n" "void test::f() { }\n" "\n" "void b(int i) {\n" " char *p = new char[10];\n" " if (i) {\n" " delete [] p;\n" " exit(0);\n" " }\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); // False positive in classmember // same code as above but the implementation is used in the // class member function check("class test {\n" " void f(int i);\n" "};\n" "void test::f(int i) {\n" " char *p = new char[10];\n" " if (i) {\n" " delete [] p;\n" " exit(0);\n" " }\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); } //# Ticket 2569 void func21() { // checking for lstat function: // ---------------------------- check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if (lstat (cpFile, &CFileAttr) != 0)\n" " {\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: cpFile\n", errout.str()); check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if (lstat (cpFile, &CFileAttr) != 0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: cpFile\n", errout.str()); check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if (lstat (cpFile, &CFileAttr) != 0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " delete [] cpFile;\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); /// checking for stat function: // ---------------------------- check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if ( stat (cpFile, &CFileAttr) != 0)\n" " {\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: cpFile\n", errout.str()); check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if ( stat (cpFile, &CFileAttr) != 0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: cpFile\n", errout.str()); check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if ( stat (cpFile, &CFileAttr) != 0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " delete [] cpFile;\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // checking for access function // http://www.gnu.org/s/libc/manual/html_node/Testing-File-Access.html // -------------------------------------------------------------------- check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if ( access (cpFile, R_OK) != 0)\n" " {\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: cpFile\n", errout.str()); check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if (access (cpFile, R_OK) != 0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: cpFile\n", errout.str()); check("void foo ()\n" "{\n" " struct stat CFileAttr;\n" " char *cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if (access (cpFile, R_OK) != 0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " delete [] cpFile;\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // checking for chdir function // http://home.fhtw-berlin.de/~junghans/cref/MAN/chdir.htm // -------------------------------------------------------- check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chdir (cpDir) != 0)\n" " {\n" " return;\n" " }\n" " delete [] cpDir;\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: cpDir\n", errout.str()); check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chdir (cpDir) != 0)\n" " {\n" " delete [] cpDir;\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: cpDir\n", errout.str()); check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chdir (cpDir) != 0)\n" " {\n" " delete [] cpDir;\n" " return;\n" " }\n" " delete [] cpDir;\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // checking for chmod function // http://publib.boulder.ibm.com/infocenter/zos/v1r10/index.jsp?topic=/com.ibm.zos.r10.bpxbd00/rtchm.htm // ------------------------------------------------------------------------------------------------------ check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chmod(cpDir, S_IRWXU|S_IRWXG) != 0)\n" " {\n" " return;\n" " }\n" " delete [] cpDir;\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: cpDir\n", errout.str()); check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chmod(cpDir, S_IRWXU|S_IRWXG) != 0)\n" " {\n" " delete [] cpDir;\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: cpDir\n", errout.str()); check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chmod(cpDir, S_IRWXU|S_IRWXG) != 0)\n" " {\n" " delete [] cpDir;\n" " return;\n" " }\n" " delete [] cpDir;\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // checking for chown function // http://publib.boulder.ibm.com/infocenter/zos/v1r10/index.jsp?topic=/com.ibm.zos.r10.bpxbd00/rtchm.htm // ------------------------------------------------------------------------------------------------------ check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chown(cpDir, 25, 0) != 0)\n" " {\n" " return;\n" " }\n" " delete [] cpDir;\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: cpDir\n", errout.str()); check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chown(cpDir, 25, 0) != 0)\n" " {\n" " delete [] cpDir;\n" " return;\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: cpDir\n", errout.str()); check("void foo()\n" "{\n" " char * cpDir = new char [7];\n" " strcpy (cpDir, \"/home/\");\n" " if (chown(cpDir, 25, 0) != 0)\n" " {\n" " delete [] cpDir;\n" " return;\n" " }\n" " delete [] cpDir;\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // checking perror //http://www.cplusplus.com/reference/clibrary/cstdio/perror/ // --------------------------------------------------------- check("void foo()\n" "{\n" " char *cBuf = new char[11];\n" " sprintf(cBuf,\"%s\",\"testtest..\");\n" " perror (cBuf);\n" " delete [] cBuf;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char *cBuf = new char[11];\n" " sprintf(cBuf,\"%s\",\"testtest..\");\n" " perror (cBuf);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: cBuf\n", errout.str()); } // # 2668 void func22() { check("void foo()\n" "{\n" " char * cpFile;\n" " cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if(freopen(cpFile,\"w\",stdout)==0)\n" " {\n" " return;\n" " }\n" " delete [] cpFile;\n" " fclose (stdout);\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: cpFile\n", errout.str()); check("void foo()\n" "{\n" " char * cpFile;\n" " cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if(freopen(cpFile,\"w\",stdout)==0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " fclose (stdout);\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (error) Memory leak: cpFile\n", errout.str()); check("void foo()\n" "{\n" " char * cpFile;\n" " cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " if(freopen(cpFile,\"w\",stdout)==0)\n" " {\n" " delete [] cpFile;\n" " return;\n" " }\n" " delete [] cpFile;\n" " fclose (stdout);\n" "}"); ASSERT_EQUALS("", errout.str()); } // # 2667 void func23() { // check open() function // ---------------------- check("int * foo()\n" "{\n" " char * cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " int file=open(cpFile,O_RDONLY);\n" " if(file < -1)\n" " {\n" " return file;\n" " }\n" " delete [] cpFile;\n" " return file;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: cpFile\n", errout.str()); check("int * foo()\n" "{\n" " char * cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " int file=open(cpFile,O_RDONLY);\n" " if(file < -1)\n" " {\n" " delete [] cpFile;\n" " return file;\n" " }\n" " return file;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: cpFile\n", errout.str()); check("int * foo()\n" "{\n" " char * cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " int file=open(cpFile,O_RDONLY);\n" " if(file < -1)\n" " {\n" " delete [] cpFile;\n" " return file;\n" " }\n" " delete [] cpFile;\n" " return file;\n" "}"); ASSERT_EQUALS("", errout.str()); // check for _open, _wopen // http://msdn.microsoft.com/en-us/library/z0kc8e3z(VS.80).aspx // ------------------------------------------------------------- check("int * foo()\n" "{\n" " char * cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " int file=_open(cpFile,_O_RDONLY);\n" " if(file == -1)\n" " {\n" " return file;\n" " }\n" " delete [] cpFile;\n" " return file;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: cpFile\n", errout.str()); check("int * foo()\n" "{\n" " char * cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " int file=_open(cpFile,_O_RDONLY);\n" " if(file == -1)\n" " {\n" " delete [] cpFile;\n" " return file;\n" " }\n" " return file;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: cpFile\n", errout.str()); check("int * foo()\n" "{\n" " char * cpFile = new char [13];\n" " strcpy (cpFile, \"testfile.txt\");\n" " int file=_open(cpFile,_O_RDONLY);\n" " if(file == -1)\n" " {\n" " delete [] cpFile;\n" " return file;\n" " }\n" " delete [] cpFile;\n" " return file;\n" "}"); ASSERT_EQUALS("", errout.str()); } // #2705 void func24() { check("void f(void)\n" "{\n" " std::string *x = new std::string;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: x\n", errout.str()); check("void f(void)\n" "{\n" " std::string *x = new std::string;\n" " delete x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void func25() { // ticket #2904 check("class Fred { };\n" "void f(void)\n" "{\n" " Fred *f = new Fred();\n" " delete f;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred { };\n" "void f(void)\n" "{\n" " Fred *f = new Fred();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: f\n", errout.str()); check("class Fred { void foo(){ } };\n" "void f(void)\n" "{\n" " Fred *f = new Fred();\n" " delete f;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred { void foo(){ } };\n" "void f(void)\n" "{\n" " Fred *f = new Fred();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: f\n", errout.str()); } void func26() { // ticket #3444 // technically there is a leak here. However warning is not wanted check("void f() {\n" " char *p = strdup(\"A=B\");\n" " putenv(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void func27() { // ticket #2773 check("void f(FRED *pData)\n" "{\n" " pData =(FRED*)malloc( 100 );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: pData\n", errout.str()); check("void f(int *pData)\n" "{\n" " pData =(int*)malloc( 100 );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: pData\n", errout.str()); } void func28() { // ticket #3236 check("void f()\n" "{\n" " my_struct *p = malloc(42);\n" " p->a + p->b;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); } void allocfunc1() { check("static char *a()\n" "{\n" " return new char[100];\n" "}\n" "static void b()\n" "{\n" " char *p = a();\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:8]: (error) Memory leak: p\n"), errout.str()); check("FILE *a()\n" "{\n" " return fopen(\"test.txt\",\"w\");\n" "}\n" "static void b()\n" "{\n" " FILE *p = a();\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:8]: (error) Resource leak: p\n"), errout.str()); check("char *a()\n" "{\n" " return malloc(10);\n" "}\n" "static void b()\n" "{\n" " char *p = a();\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:8]: (error) Memory leak: p\n"), errout.str()); } void allocfunc2() { check("static char *a(int size)\n" "{\n" " return new char[size];\n" "}\n" "static void b()\n" "{\n" " int len = 100;\n" " char *p = a(len);\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *a(char *a)\n" "{\n" " return realloc(a, 10);\n" "}\n" "static void b()\n" "{\n" " char *p = a(0);\n" " char *q = a(p);\n" " if (q)\n" " free(q);\n" " else\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *a()\n" "{\n" " return malloc(10);\n" "}\n" "static void b()\n" "{\n" " char *p = a();\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc3() { check("static char *a()\n" "{\n" " char *data = malloc(10);;" " return data;\n" "}\n" "static void b()\n" "{\n" " char *p = a();\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:8]: (error) Memory leak: p\n"), errout.str()); } void allocfunc4() { check("char* foo()\n" "{\n" " char *str = NULL;\n" " str = realloc( str, 20 );\n" " return str;\n" "}\n" "\n" "void bar()\n" "{\n" " char *p = foo();\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Memory leak: p\n"), errout.str()); check("char* foo()\n" "{\n" " char *str = NULL;\n" " str = realloc( str, 20 );\n" " return str;\n" "}\n" "\n" "void bar()\n" "{\n" " char *p = foo();\n" " delete p;\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Mismatching allocation and deallocation: p\n"), errout.str()); } void allocfunc5() { check("void foo(char **str)\n" "{\n" " *str = malloc(20);\n" "}\n" "\n" "void bar()\n" "{\n" " char *p;\n" " foo(&p);\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:10]: (error) Memory leak: p\n"), errout.str()); check("void foo(char **str)\n" "{\n" " *str = malloc(20);\n" "}\n" "\n" "void bar()\n" "{\n" " char *p;\n" " foo(&p);\n" " delete p;\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:10]: (error) Mismatching allocation and deallocation: p\n"), errout.str()); check("void foo(char **q, char **str)\n" "{\n" " *str = malloc(20);\n" "}\n" "\n" "void bar()\n" "{\n" " char *p;\n" " char *q;\n" " foo(&q, &p);\n" "}"); ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Memory leak: p\n"), errout.str()); check("void foo(char **str)\n" "{\n" " char *a = malloc(20);\n" " *str = a;\n" "}\n" "\n" "void bar()\n" "{\n" " char *p;\n" " foo(&p);\n" "}"); TODO_ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Memory leak: p\n"), "", errout.str()); check("void foo(char **str)\n" "{\n" " free(*str);\n" " *str = malloc(20);\n" "}\n" "\n" "void bar()\n" "{\n" " char *tmp = malloc(10);\n" " foo(&tmp);\n" " foo(&tmp);\n" " free(tmp);\n" "}"); ASSERT_EQUALS("", errout.str()); //#ticket 1789: getcode other function: check("void foo(char **str)\n" "{\n" " if (*str == NULL)\n" " *str = malloc(20)\n;" " else\n" " *str = realloc(*str, 20)\n;" "}\n" "\n" "void bar()\n" "{\n" " char *tmp = malloc(10);\n" " foo(&tmp);\n" " foo(&tmp);\n" " free(tmp);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int alloc(char **str) {\n" " *str = malloc(20);\n" " if (condition) { free(str); return -123; }\n" " return 0;\n" "}\n" "\n" "void bar()\n" "{\n" " char *p;\n" " if ((ret = alloc(&p)) != 0) return;\n" " free(p);\n" "}"); ASSERT_EQUALS(std::string(), errout.str()); } void allocfunc6() { check("static FILE* data()\n" "{\n" " return fopen(\"data.txt\",\"rt\");\n" "}\n" "\n" "static void foo()\n" "{\n" " char* expr;\n" " func(&expr);\n" "\n" " FILE *f = data();\n" " fclose(f);\n" "\n" " free(expr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc7() { // Ticket #2374 - no false positive check("char *data()\n" "{\n" " char *s = malloc(100);\n" " strings[0] = s;\n" " return s;\n" "}\n" "\n" "static void foo()\n" "{\n" " char* s = data();\n" "}"); ASSERT_EQUALS("", errout.str()); // #5144 check("C* BuildC( C *previous ) {\n" " C *result = malloc(100);\n" " result->previous = previous;\n" " return result;\n" "}\n" "C *ParseC( ) {\n" " C *expr1 = NULL;\n" " while( something() )\n" " expr1 = BuildC(expr1);\n" " return expr1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc8() { // Ticket #2213 - calling alloc function twice check("FILE *foo() {\n" " return fopen(a,b);\n" "}\n" "\n" "void bar() {\n" " FILE *f = foo();\n" " f = foo();\n" " fclose(f);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Resource leak: f\n", errout.str()); } void allocfunc9() { // Ticket #2673 - address is taken in alloc func check("char *addstring(const char *s) {\n" " char *ret = strdup(s);\n" " strings.push_back(ret);" " return ret;\n" "}\n" "\n" "void foo() {\n" " char *s = addstring(\"abc\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc10() { // Ticket #2921 - static pointer check("char *getstr() {\n" " static char *ret = malloc(100);\n" " return ret;\n" "}\n" "\n" "void foo() {\n" " char *s = getstr();\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc11() { // ticket #3809 - false positive check("void f (double * & data_val) {\n" " data_val = malloc(0x100);\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc12() { // #3660: allocating and returning non-local pointer => not allocfunc check("char *p;\n" // global pointer "char *a() {\n" " if (!p) p = malloc(10);\n" " return p;\n" "}\n" "void b() {\n" " char *x = a();\n" "}"); ASSERT_EQUALS("", errout.str()); } void allocfunc13() { // #4494: class function check("namespace n {\n" " char *a() { return malloc(100); }\n" "}\n" "void b() {\n" " char *x = n::a();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: x\n", errout.str()); check("class C {\n" // ¤4540 " char *a() { return malloc(100); }\n" "}\n" "void b() {\n" " C c;" " char *x = c.a();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: x\n", errout.str()); check("class Pool{\n" " int* GetNewObj()\n" " {\n" " return new int;\n" " }\n" "};\n" "void foo(){\n" " Pool pool;\n" " int* a = pool.GetNewObj();\n" " int* b = GetNewObj();\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: a\n", errout.str()); } void allocfunc14() { // use pointer before returning it check("static struct ABC * newabc() {\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " init_abc(&abc->a);\n" // <- might take address " return abc;\n" "}\n" "\n" "static void f() {\n" " struct ABC *abc = newabc();\n" "}"); ASSERT_EQUALS("", errout.str()); } void inlineFunction() { // #3989 - inline function 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()); } void throw1() { check("void foo()\n" "{\n" " char *str = new char[10];\n" " if ( ! abc )\n" " throw 123;\n" " delete [] str;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: str\n", errout.str()); } void throw2() { check("void foo()\n" "{\n" " char *str = 0;\n" " try\n" " {\n" " str = new char[100];\n" " if ( somecondition )\n" " throw exception;\n" " delete [] str;\n" " }\n" " catch ( ... )\n" " {\n" " delete [] str;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void linux_list_1() { check("struct AB\n" "{\n" " int a;\n" " int b;\n" "};\n" "void foo()\n" "{\n" " struct AB *ab = new AB;\n" " func(&ab->a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void linux_list_2() { // #5993 check("void foo() {\n" " struct AB *ab = malloc(sizeof(struct AB));\n" " list_add_tail(&(ab->list));\n" "}"); ASSERT_EQUALS("", errout.str()); } void sizeof1() { check("void f()\n" "{\n" " struct s_t s1;\n" " struct s_t cont *p = &s1;\n" " struct s_t *s2;\n" "\n" " memset(p, 0, sizeof(*p));\n" "\n" " s2 = (struct s_t *) malloc(sizeof(*s2));\n" "\n" " if (s2->value != 0)\n" " return;\n" "\n" " free(s2);\n" "\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (error) Memory leak: s2\n", errout.str()); } 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" "[test.cpp:5]: (error) Memory leak: a\n", errout.str()); } void realloc2() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = (char *)realloc(a, 100);\n" " free(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc3() { check("void foo()\n" "{\n" " char *a = 0;\n" " if ((a = realloc(a, 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc4() { check("void foo()\n" "{\n" " static char *a = 0;\n" " if ((a = realloc(a, 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n", "[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc5() { check("void foo()\n" "{\n" " char *buf;\n" " char *new_buf;\n" " buf = calloc( 10 );\n" " new_buf = realloc ( buf, 20);\n" " if ( !new_buf )\n" " free(buf);\n" " else\n" " free(new_buf);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc6() { ASSERT_EQUALS(";;realloc;;", getcode("char *buf; buf=realloc(buf,100);", "buf")); ASSERT_EQUALS(";;alloc;", getcode("char *buf; buf=realloc(0,100);", "buf")); } 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" "}"); ASSERT_EQUALS("", errout.str()); } void realloc13() { check("void foo()\n" "{\n" " char **str;\n" " *str = realloc(*str,100);\n" " free (*str);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'str\' nulled but not freed upon failure\n", errout.str()); } void realloc14() { check("void foo() {\n" " char *p;\n" " p = realloc(p, size + 1);\n" " if (!p)\n" " error();\n" " usep(p);\n" "}\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" "[test.cpp:6]: (error) Memory leak: m_options\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 assign1() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = 0;\n" " free(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: a\n", errout.str()); check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " char *p = a;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " char *p = a + 1;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a += 10;\n" " free(a - 10);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = (void *)a + 10;\n" " free(a - 10);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char *a = new char[100];\n" " list += a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid() { check("void foo()\n" "{\n" " char *p = malloc(100);\n" " {\n" " char *p = 0;\n" " delete p;\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void cast1() { check("void foo()\n" "{\n" " char *a = reinterpret_cast(malloc(10));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: a\n", errout.str()); } void dealloc_use() { // It is ok to take the address.. check("void f()\n" "{\n" " char *s = new char[100];\n" " delete [] s;\n" " p = s;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *s = new char[100];\n" " delete [] s;\n" " foo(s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *s = new char[100];\n" " delete [] s;\n" " printf(\"%p\\n\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); // The pointer to the pointer is valid.. check("void f()\n" "{\n" " char *str;\n" " free(str);\n" " foo(&str);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char *str = 0;\n" " free(str);\n" " f1(&str);\n" " f2(str);\n" "}"); ASSERT_EQUALS("", errout.str()); // Dereferencing the freed pointer is not ok.. check("void foo()\n" "{\n" " char *str = malloc(10);\n" " free(str);\n" " char c = *str;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Dereferencing 'str' after it is deallocated / released\n", "", errout.str()); check("void foo()\n" "{\n" " char *str = malloc(10);\n" " free(str);\n" " char c = str[10];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Dereferencing 'str' after it is deallocated / released\n", errout.str()); check("void foo()\n" "{\n" " char *str = malloc(10);\n" " free(str);\n" " str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Dereferencing 'str' after it is deallocated / released\n", errout.str()); check("void foo() {\n" " char *str = malloc(10);\n" " free(str);\n" " strcpy(str, p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'str' after it is deallocated / released\n", errout.str()); check("void foo(int x) {\n" " char *str = malloc(10);\n" " free(str);\n" " assert(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void dealloc_use_2() { // #3041 - assigning pointer when it's used check("void f(char *s) {\n" " free(s);\n" " strcpy(a, s=b());\n" "}"); ASSERT_EQUALS("", errout.str()); } void dealloc_use_3() { check("void foo()\n" "{\n" " char *str = malloc(10);\n" " realloc(str, 0);\n" " str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Dereferencing 'str' after it is deallocated / released\n", errout.str()); check("void foo()\n" "{\n" " char *str = realloc(0, 10);\n" " realloc(str, 0);\n" " str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Dereferencing 'str' after it is deallocated / released\n", errout.str()); } void dealloc_use_4() { // #7960 check("class SR {\n" "public:\n" " void dostuff();\n" " int* m_data;\n" "};\n" "void SR::dostuff() {\n" " SR sr;\n" " delete m_data;\n" " sr.m_data = new SVec;\n" " *m_data = 123;\n" "}"); ASSERT_EQUALS("", errout.str()); } void freefree1() { check("void foo()\n" "{\n" " char *str = malloc(100);\n" " free(str);\n" " free(str);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Deallocating a deallocated pointer: str\n", errout.str()); } void freefree2() { check("void foo()\n" "{\n" " FILE *fd = fopen(\"test.txt\", \"wb\");\n" " fprintf(fd, \"test\");\n" " fclose(fd);\n" "}"); ASSERT_EQUALS("", errout.str()); } void freefree3() { check("void foo()\n" "{\n" " char *p = malloc(10);\n" " free(p);\n" " bar(&p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void strcpy_result_assignment() { check("void foo()\n" "{\n" " char *p1 = malloc(10);\n" " char *p2 = strcpy(p1, \"a\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void strcat_result_assignment() { check("void foo()\n" "{\n" " char *p = malloc(10);\n" " p[0] = 0;\n" " p = strcat( p, \"a\" );\n" " free( p );\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void all1() { check("void foo()\n" "{\n" " Fred *f = new Fred;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void malloc_constant_1() { check("void foo()\n" "{\n" " int *p = malloc(3);\n" " free(p);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) The allocated size 3 is not a multiple of the underlying type's size.\n", errout.str()); } void unknownFunction1() { check("void foo()\n" "{\n" " int *p = new int[100];\n" " if (abc)\n" " {\n" " delete [] p;\n" " ThrowException();\n" " }\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void unknownFunction2() { check("void foo()\n" "{\n" " int *p = new int[100];\n" " if (abc)\n" " {\n" " delete [] p;\n" " ThrowException();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Memory leak: p\n", errout.str()); check("void foo()\n" "{\n" " int *p = new int[100];\n" " p = g();\n" " delete [] p;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: p\n", errout.str()); } void unknownFunction4() { check("void foo()\n" "{\n" " int *p = new int[100];\n" " a();\n" " if (b) return;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); } void unknownFunction5() { check("static void foo()\n" "{\n" " char *p = NULL;\n" "\n" " if( a )\n" " p = malloc(100);\n" "\n" " if( a )\n" " {\n" " FREENULL(p);\n" " FREENULL();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void class1() { check("class Fred\n" "{\n" "public:\n" " Fred()\n" " {\n" " int *p = new int[100];\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: p\n", errout.str()); } void class2() { check("class Fred {\n" "public:\n" " Fred() : rootNode(0) {}\n" "\n" "private:\n" " struct Node {\n" " Node(Node* p) {\n" " parent = p;\n" " if (parent) {\n" " parent->children.append(this);\n" " }\n" " }\n" "\n" " ~Node() {\n" " qDeleteAll(children);\n" " }\n" "\n" " QList children;\n" " };\n" "\n" " Node rootNode;\n" "\n" " void f() {\n" " Node* recordNode = new Node(&rootNode);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void autoptr1() { check("std::auto_ptr foo()\n" "{\n" " int *i = new int;\n" " return std::auto_ptr(i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void if_with_and() { check("void f()\n" "{\n" " char *a = new char[10];\n" " if (!a && b() )\n" " return;\n" "\n" " delete [] a;\n" "}", false, false, true); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *a = new char[10];\n" " if (b() && !a )\n" " return;\n" "\n" " delete [] a;\n" "}", false, false, true); ASSERT_EQUALS("", errout.str()); } void assign_pclose() { check("void f() {\n" " FILE *f = popen (\"test\", \"w\");\n" " int a = pclose(f);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); } void conditional_dealloc_return() { // #7820 check("void f() {\n" " FILE *pPipe = popen(\"foo\", \"r\");\n" " if (feof(pPipe))\n" " pclose(pPipe);\n" " return;\n" "}", /*c=*/true, /*posix=*/true); ASSERT_EQUALS("[test.c:5]: (error) Resource leak: pPipe\n", errout.str()); check("extern int bar();\n" "void f() {\n" " char *c = (char*) malloc(10);\n" " if (bar())\n" " free(c);\n" " return;\n" "}", /*c=*/true); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: c\n", errout.str()); check("extern int bar();\n" "extern int baz();\n" "extern void bos(char*);\n" "void f() {\n" " char *c;\n" " if(bar()) {\n" " bos(c);\n" " c = (char*) malloc(10);\n" " if (baz())\n" " free(c);\n" " };\n" "}", /*c=*/true); ASSERT_EQUALS("[test.c:11]: (error) Memory leak: c\n", errout.str()); } void exit2() { check("void f()\n" "{\n" " char *out = new char[100];\n" " exit(0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *out = new char[100];\n" " if( out ) {}\n" " exit(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void exit4() { check("void f()\n" "{\n" " char *p = malloc(100);\n" " if (x)\n" " {\n" " exit(0);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: p\n", errout.str()); } void exit5() { check("void f()\n" "{\n" " char *p = malloc(100);\n" " if (p)\n" " {\n" " xyz();\n" " exit(0);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void exit6() { check("int main(int argc, char *argv[]) {\n" " FILE *sfile;\n" " unsigned long line;\n" " sfile = fopen(\"bar\", \"r\");\n" " if (!sfile)\n" " return 1;\n" " for(line = 0; ; line++) {\n" " if (argc > 3)\n" " break;\n" " exit(0);\n" " }\n" " fclose(sfile);\n" " exit(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void exit7() { check("int a(int x) {\n" " if (x == 0) {\n" " exit(0);\n" " }\n" " return x + 2;\n" "}\n" "\n" "void b() {\n" " char *p = malloc(100);\n" " int i = a(123);\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: p\n", errout.str()); } void noreturn() { check("void fatal_error()\n" "{ exit(1); }\n" "\n" "void f()\n" "{\n" " char *p = malloc(100);\n" " fatal_error();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void fatal_error()\n" // #2440 "{ }\n" "\n" "void f()\n" "{\n" " char *p = malloc(100);\n" " fatal_error();\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: p\n", errout.str()); } void strndup_function() { check("void f() {\n" " char *out = strndup(\"text\", 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: out\n", errout.str()); } void tmpfile_function() { check("void f()\n" "{\n" " FILE *f = tmpfile();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: f\n", errout.str()); check("void f()\n" "{\n" " FILE *f = tmpfile();\n" " if (!f)\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Resource leak: f\n", errout.str()); check("void f()\n" "{\n" " FILE *f = tmpfile();\n" " fclose(f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " FILE *f = tmpfile();\n" " if (!f)\n" " return;\n" " fclose(f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("FILE *f()\n" "{\n" " return tmpfile();\n" "}"); ASSERT_EQUALS("", errout.str()); } void fcloseall_function() { check("void f()\n" "{\n" " FILE *f = fopen(fname, str);\n" " fcloseall();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " FILE *f = tmpfile();\n" " fcloseall();\n" "}"); ASSERT_EQUALS("", errout.str()); } void open_function() { check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" "}", false, true, true); ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fd\n", errout.str()); check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" " if (fd == -1)\n" " return;\n" " close(fd);\n" "}", false, true, true); ASSERT_EQUALS("", errout.str()); check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" " if (fd < 0)\n" " return;\n" " close(fd);\n" "}", false, true, true); ASSERT_EQUALS("", errout.str()); check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" " if (-1 == fd)\n" " return;\n" " close(fd);\n" "}", false, true, true); ASSERT_EQUALS("", errout.str()); } void creat_function() { check("void f(const char *path)\n" "{\n" " int fd = creat(path, S_IRWXU);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fd\n", errout.str()); } void close_function() { check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" " close(fd);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); check("void f(const char *path)\n" "{\n" " int fd = creat(path, S_IRWXU);\n" " close(fd);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); check("void f(const char *path)\n" "{\n" " int fd = creat(path, S_IRWXU);\n" " if (close(fd) < 0) {\n" " perror(\"close\");\n" " }\n" "}", false, true); ASSERT_EQUALS("", errout.str()); //#ticket 1401 check("int myfunc()\n" "{\n" " int handle;\n" "\n" " handle = open(\"myfile\");\n" " if (handle < 0) return 1;\n" "\n" " while (some_condition())\n" " if (some_other_condition())\n" " {\n" " close(handle);\n" " return 3;\n" " }\n" " close(handle);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); //#ticket 1401 check("int myfunc()\n" "{\n" " int handle;\n" "\n" " handle = open(\"myfile\", O_RDONLY);\n" " if (handle < 0) return 1;\n" "\n" " while (some_condition())\n" " if (some_other_condition())\n" " {\n" " return 3;\n" " }\n" " close(handle);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:11]: (error) Resource leak: handle\n", errout.str()); } void fd_functions() { check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" " read(fd, buf, count);\n" " readv(fd, iov, iovcnt);\n" " readahead(fd, offset, count);\n" " pread(fd, buf, count, offset);\n" " write(fd, buf, count);\n" " writev(fd, iov, iovcnt);\n" " pwrite(fd, buf, count, offset);\n" " ioctl(fd, request);\n" " posix_fallocate(fd, offset, len);\n" " posix_fadvise(fd, offset, len, advise);\n" " fsync(fd);\n" " fdatasync(fd);\n" " sync_file_range(fd, offset, nbytes, flags);\n" " lseek(fd, offset, whence);\n" " fcntl(fd, cmd);\n" " flock(fd, op);\n" " lockf(fd, cmd, len);\n" " ftruncate(fd, len);\n" " fstat(fd, buf);\n" " fchmod(fd, mode);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:24]: (error) Resource leak: fd\n", errout.str()); } void file_functions() { check("void f()\n" "{\n" "FILE *f = fopen(fname, str);\n" "feof(f);\n" "clearerr(in);\n" "ferror(in);\n" "fread(ptr, 10, 1, in);\n" "fwrite(ptr, 10, 1, in);\n" "fflush(in);\n" "setbuf(in, buf);\n" "setbuffer(in, buf, 100);\n" "setlinebuf(in);\n" "setvbuf(in, buf, _IOLBF, 0);\n" "fseek(in, 10, SEEK_SET);\n" "fseeko(in, 10, SEEK_SET);\n" "ftell(in);\n" "ftello(in);\n" "rewind(in);\n" "fsetpos(in, 0);\n" "fgetpos(in, 10);\n" "fprintf(in, \"text\\n\");\n" "}"); ASSERT_EQUALS("[test.cpp:22]: (error) Resource leak: f\n", errout.str()); } void getc_function() { { check("void f()\n" "{" " int c;\n" " FILE *fin1a = fopen (\"FILE.txt\", \"r\");\n" " while ( (c = getc (fin1a)) != EOF)\n" " { }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Resource leak: fin1a\n", errout.str()); } { check("void f()\n" "{\n" " int c;\n" " FILE *fin1b = fopen(\"FILE.txt\", \"r\");\n" " c = getc(fin1b);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Resource leak: fin1b\n", errout.str()); } } void pointer_to_pointer() { check("void f(char **data)\n" "{\n" " char *c = new char[12];\n" " *c = 0;\n" " *data = c;\n" "}"); ASSERT_EQUALS("", errout.str()); } void dealloc_and_alloc_in_func() { check("char *f( const char *x )\n" "{\n" " delete [] x;\n" " return new char[10];\n" "}\n" "\n" "int main()\n" "{\n" " char *a=0;\n" " a = f( a );\n" " a[0] = 1;\n" " delete [] a;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void unknownSyntax1() { // I don't know what this syntax means so cppcheck should bail out check("void foo()\n" "{\n" " void *sym = ( {\n" " void *__ptr = malloc(100);\n" " if(!__ptr && 100 != 0)\n" " {\n" " exit(1);\n" " }\n" " __ptr;\n" " } );\n" " free(sym);\n" "}"); ASSERT_EQUALS("", errout.str()); } void knownFunctions() { check("void foo()\n" "{\n" " int *p = new int[100];\n" " typeid(p);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); } void same_function_name() { check("void a(char *p)\n" "{ }\n" "void b()\n" "{\n" " char *p = malloc(10);\n" " abc.a(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void functionParameter() { check("void a(char *p)\n" "{\n" " p = malloc(100);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: p\n", errout.str()); } // Ticket #2014 - setjmp / longjmp void jmp() { check("int main()\n" "{\n" " jmp_buf env;\n" " int val;\n" " char *a;\n" "\n" " val = setjmp(env);\n" " if(val)\n" " {\n" " delete a;\n" " return 0;\n" " }\n" "\n" " a = new char(1);\n" " longjmp(env, 1);\n" "\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void trac1949() { check("int fn()\n" "{\n" " char * buff = new char[100];\n" " assert (buff);\n" " return 0;\n" "}" ); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: buff\n", errout.str()); } void trac2540() { check("void f()\n" "{\n" " char* str = strdup(\"abc def\");\n" " char *name = strtok(str, \" \");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: str\n", errout.str()); check("void f(char *cBuf)\n" "{\n" " char* str = strdup(*cBuf);\n" " char *name = strtok(str, \" \");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: str\n", errout.str()); } void trac2662() { // segfault because of endless recursion // call_func -> getAllocationType -> functionReturnType -> call_func .. check("char *foo() {\n" " return foo();\n" "}\n" "\n" "void bar() {\n" " char *s = foo();\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *foo() {\n" " char *s = foo();\n" " return s;\n" "}\n" "\n" "void bar() {\n" " char *s = foo();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int shl()\n" "{\n" " int a = shr();\n" " return a;\n" "}\n" "\n" "int shr()\n" "{\n" " return shl();\n" "}"); ASSERT_EQUALS("", errout.str()); } void trac1879() { // #1879 non regression test case check("void test() {\n" "int *a = new int[10];\n" "try {}\n" "catch(...) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n", errout.str()); } void ptrptr() { check("void f() {\n" " char *p;\n" " char **pp = &p;\n" " *pp = calloc(10, 1);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); } void c_code() { check("int main(void) {\n" " struct llist *ll = malloc(sizeof(struct llist));\n" " free(ll);\n" " ll = NULL;\n" " delete(ll, ll->top);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void gnucfg() { Settings settings; LOAD_LIB_2(settings.library, "gnu.cfg"); const char code[] = "void leak() {\n" " char * p = get_current_dir_name();\n" // memory leak "}\n" "void noLeak() {\n" " char * p = get_current_dir_name();\n" " free(p)\n;" "}"; check(code, false, true, false, &settings); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void trac3991() { check("int read_chunk_data(char **buffer) {\n" " *buffer = (char *)malloc(chunk->size);\n" " if (*buffer == NULL)\n" " return -1;\n" " return 0;\n" "}\n" "void printf_chunk_recursive() {\n" " UINT8 *data = NULL;\n" " int avierr = read_chunk_data(&data);\n" " if (avierr == 0)\n" " free(data);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void crash() { check("class ComponentDC {\n" " ::Component * getComponent();\n" "};\n" "::Component * ComponentDC::getComponent() {\n" " return ((::Component *)myComponent);\n" "}\n" "class MultiComponentDC : public ComponentDC {\n" " virtual void addChild(InterfaceNode *);\n" "};\n" "void MultiComponentDC::addChild(InterfaceNode *childNode) {\n" " ComponentDC *cdc = dynamic_cast(childNode);\n" " if (cdc)\n" " ::Component *c = cdc->getComponent();\n" "}"); ASSERT_EQUALS("", errout.str()); } void trac7680() { check("void foo() {\n" " int *i = ::new int;\n" " ::delete i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void trac7440() { check("int main(void) {\n" " char* data = new char[100];\n" " char** dataPtr = &data;\n" " printf(\"test\");\n" " delete [] *dataPtr;\n" "}"); ASSERT_EQUALS("", 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() { 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() { 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); // 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()); } // 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"); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakNoVar checkMemoryLeakNoVar(&tokenizer, &settings, this); checkMemoryLeakNoVar.check(); } void run() { settings.inconclusive = true; settings.standards.posix = true; 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); } 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()); } 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" " (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 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()); } }; REGISTER_TEST(TestMemleakNoVar) class TestMemleakGLib : public TestFixture { public: TestMemleakGLib() : TestFixture("TestMemleakGLib") { } private: Settings settings; void check(const char code[]) { // Clear the error buffer.. errout.str(""); std::istringstream istr(code); std::vector files; files.push_back("test.cpp"); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess... Preprocessor preprocessor(settings, this); const simplecpp::TokenList &tokens2 = preprocessor.preprocess(tokens1, "", files); // Tokenizer.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(&tokens2); tokenizer.simplifyTokenList1(files[0].c_str()); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak1(&tokenizer, &settings, this); CheckMemoryLeakInClass checkMemoryLeak2(&tokenizer, &settings, this); CheckMemoryLeakStructMember checkMemoryLeak3(&tokenizer, &settings, this); CheckMemoryLeakNoVar checkMemoryLeak4(&tokenizer, &settings, this); checkMemoryLeak1.check(); checkMemoryLeak1.checkReallocUsage(); checkMemoryLeak2.check(); checkMemoryLeak3.check(); checkMemoryLeak4.check(); } void run() { LOAD_LIB_2(settings.library, "gtk.cfg"); settings.addEnabled("all"); TEST_CASE(glib1); TEST_CASE(glib2); // #2806 - FP when using redundant assignment } void glib1() { check("void f(gchar *_a, gchar *_b) {" " g_return_if_fail(_a);" " gchar *a = g_strdup(_a);" " g_return_if_fail(_b);" " gchar *b = g_strdup(_b);" " g_free(a);" " g_free(b);" "}"); ASSERT_EQUALS("[test.cpp:1]: (error) Memory leak: a\n", errout.str()); check("void x() {\n" " g_strlcpy(a, g_strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with g_strdup, g_strlcpy doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " if(TRUE || g_strcmp0(g_strdup(a), b));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with g_strdup, g_strcmp0 doesn't release it.\n", errout.str()); check("void foo()\n" "{\n" " int *p = g_malloc(3);\n" " g_free(p);\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) The allocated size 3 is not a multiple of the underlying type's size.\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" " g_free(str1);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:12]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " gchar *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { g_free(s); }\n" " void xy()\n" " { s = g_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()); } void glib2() { // #2806 - FP when there is redundant assignment check("void foo() {\n" " gchar *bar;\n" " bar = g_strdup(fuba);\n" " bar = g_strstrip(bar);\n" " g_free(bar);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* foo()\n" "{\n" " char *p1 = g_malloc(10);\n" " char *p2 = g_strlcpy(p1, \"a\");\n" " return p2;\n" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:5]: (error) Memory leak: p1\n", errout.str()); } }; REGISTER_TEST(TestMemleakGLib) class TestMemleakWindows : public TestFixture { public: TestMemleakWindows() : TestFixture("TestMemleakWindows") { } 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"); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, this); checkMemoryLeak.checkReallocUsage(); checkMemoryLeak.check(); } void run() { LOAD_LIB_2(settings.library, "windows.cfg"); TEST_CASE(openfileNoLeak); TEST_CASE(returnValueNotUsed_tfopen_s); TEST_CASE(sendMessage); } void openfileNoLeak() { check("void f() {" " OFSTRUCT OfStr;" " int hFile = OpenFile(\"file\", &OfStr, 0);" "}"); ASSERT_EQUALS("[test.c:1]: (error) Resource leak: hFile\n", errout.str()); check("void f() {" " OFSTRUCT OfStr;" " int hFile = OpenFile(\"file\", &OfStr, OF_EXIST);" "}"); TODO_ASSERT_EQUALS("", "[test.c:1]: (error) Resource leak: hFile\n", errout.str()); } void returnValueNotUsed_tfopen_s() { check("bool LoadFile(LPCTSTR filename) {\n" " FILE *fp = NULL;\n" " _tfopen_s(&fp, filename, _T(\"rb\"));\n" " if (!fp)\n" " return false;\n" " fclose(fp);\n" " return true;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool LoadFile(LPCTSTR filename) {\n" " FILE *fp = NULL;\n" " _tfopen_s(&fp, filename, _T(\"rb\"));\n" "}"); TODO_ASSERT_EQUALS("[test.c:3]: (error) Resource leak: fp\n", "", errout.str()); } void sendMessage() { check("void SetFont() {\n" " HFONT hf = CreateFontIndirect(&lf);\n" " SendMessage(hwnd, WM_SETFONT, (WPARAM)hf, TRUE);\n" // We do not know what the handler for the message will do with 'hf', so it might be closed later "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestMemleakWindows) cppcheck-1.82/test/testnullpointer.cpp000066400000000000000000002634771322667425100202150ustar00rootroot00000000000000/* * 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 "checknullpointer.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() { // Load std.cfg configuration { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); } 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(nullpointer_addressOf); // address of TEST_CASE(nullpointerSwitch); // #2626 TEST_CASE(nullpointer_cast); // #4692 TEST_CASE(nullpointer_castToVoid); // #3771 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(nullpointerExit); TEST_CASE(nullpointerStdString); TEST_CASE(nullpointerStdStream); 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); } 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); tokenizer.simplifyTokenList2(); checkNullPointer.runSimplifiedChecks(&tokenizer, &settings, this); } void checkP(const char code[]) { // Clear the error buffer.. errout.str(""); settings.inconclusive = false; // Raw tokens.. std::vector files; files.push_back("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); tokenizer.simplifyTokenList2(); checkNullPointer.runSimplifiedChecks(&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()); } // 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 #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" "}"); 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" "int main(){f(0);}\n", true); 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 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" "[test.cpp:7]: (error) Null pointer dereference\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()); } // 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()); } 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 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: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" /*"[test.cpp:11]: (error) Possible null pointer dereference: p\n" "[test.cpp:12]: (error) Possible null pointer dereference: p\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 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 *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(\"%d\", p);\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) 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:3] -> [test.cpp:2]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str()); } }; REGISTER_TEST(TestNullPointer) cppcheck-1.82/test/testoptions.cpp000066400000000000000000000054411322667425100173160ustar00rootroot00000000000000// 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 "options.h" #include "testsuite.h" class TestOptions: public TestFixture { public: TestOptions() :TestFixture("TestOptions") { } private: void run() { TEST_CASE(which_test); TEST_CASE(which_test_method); TEST_CASE(no_test_method); TEST_CASE(not_quiet); TEST_CASE(quiet); TEST_CASE(multiple_testcases); TEST_CASE(invalid_switches); } void which_test() const { const char* argv[] = {"./test_runner", "TestClass"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS("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_EQUALS("TestClass::TestMethod", args.which_test()); } void no_test_method() const { const char* argv[] = {"./test_runner"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS("", 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 multiple_testcases() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "Ignore::ThisOne"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS("TestClass::TestMethod", 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); ASSERT_EQUALS("TestClass::TestMethod", args.which_test()); ASSERT_EQUALS(true, args.quiet()); } }; REGISTER_TEST(TestOptions) cppcheck-1.82/test/testother.cpp000066400000000000000000007122251322667425100167510ustar00rootroot00000000000000/* * 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 "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() { 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(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(oldStylePointerCast); TEST_CASE(invalidPointerCast); TEST_CASE(passedByValue); TEST_CASE(passedByValue_nonConst); 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(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(duplicateExpressionTernary); // #6391 TEST_CASE(duplicateExpressionTemplate); // #6930 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_7133); TEST_CASE(redundantVarAssignment_stackoverflow); TEST_CASE(redundantMemWrite); TEST_CASE(varFuncNullUB); TEST_CASE(checkPipeParameterSize); // ticket #3521 TEST_CASE(checkCastIntToCharAndBack); // ticket #160 TEST_CASE(checkCommaSeparatedReturn); 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(partiallyMoved); TEST_CASE(moveAndLambda); TEST_CASE(forwardAndUsed); TEST_CASE(funcArgNamesDifferent); TEST_CASE(funcArgOrderDifferent); TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {" } void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, Settings* settings = 0) { // 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; // 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); if (runSimpleChecks) { tokenizer.simplifyTokenList2(); checkOther.runSimplifiedChecks(&tokenizer, settings, this); } } void check(const char code[], Settings *s) { check(code,"test.cpp",false,true,true,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; files.push_back(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); tokenizer.simplifyTokenList2(); checkOther.runSimplifiedChecks(&tokenizer, settings, this); } void checkposix(const char code[]) { static Settings settings; settings.addEnabled("warning"); settings.standards.posix = true; check(code, nullptr, // filename false, // experimental false, // inconclusive true, // runSimpleChecks &settings); } void checkInterlockedDecrement(const char code[]) { static Settings settings; settings.platformType = Settings::Win32A; check(code, nullptr, false, false, true, &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 = 0;\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 zeroDivCond() { check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x > 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>0' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x >= 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>=1' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(int x) {\n" " int y = 17 / x;\n" " if (x == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x==0' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 2.\n", errout.str()); // function call check("void f1(int x, int y) { c=x/y; }\n" "void f2(unsigned int y) {\n" " f1(123,y);\n" " if (y>0){}\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (warning) Either the condition 'y>0' is redundant or there is division by zero at line 1.\n", errout.str()); // avoid false positives when variable is changed after division check("void f() {\n" " unsigned int x = do_something();\n" " int y = 17 / x;\n" " x = some+calculation;\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); { // function is called that might modify global variable check("void do_something();\n" "int x;\n" "void f() {\n" " int y = 17 / x;\n" " do_something();\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // function is called. but don't care, variable is local check("void do_something();\n" "void f() {\n" " int x = some + calculation;\n" " int y = 17 / x;\n" " do_something();\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 4.\n", errout.str()); } check("void do_something(int value);\n" "void f(int x) {\n" " int y = 17 / x;\n" " do_something(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int x;\n" "void f() {\n" " int y = 17 / x;\n" " while (y || x == 0) { x--; }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket 5033 segmentation fault (valid code) in CheckOther::checkZeroDivisionOrUselessCondition check("void f() {\n" "double* p1= new double[1];\n" "double* p2= new double[1];\n" "double* p3= new double[1];\n" "double* pp[3] = {p1,p2,p3};\n" "}"); ASSERT_EQUALS("", errout.str()); // #5105 - FP check("int f(int a, int b) {\n" " int r = a / b;\n" " if (func(b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Unknown types for b and c --> do not warn check("int f(int d) {\n" " int r = (a?b:c) / d;\n" " if (d == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a) {\n" " int r = a ? 1 / a : 0;\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a) {\n" " int r = (a == 0) ? 0 : 1 / a;\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } 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 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; // 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" "}"); TODO_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", "[test.cpp:3]: (portability) Casting between float* and 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 integer* and long double* which have an incompatible binary data representation.\n" "[test.cpp:3]: (portability) Casting between integer* and double* which have an incompatible binary data representation.\n" "[test.cpp:4]: (portability) Casting between integer* 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 integer* 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 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 integer* 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 reference.\n", errout.str()); check("void f(std::unique_ptr ptr) {}"); ASSERT_EQUALS("", 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 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 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 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 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 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 reference.\n", errout.str()); check("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by 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 reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by 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 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 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 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 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 reference.\n", errout.str()); check("void f(std::string str) {\n" " std::string& s2 = str;\n" "}"); ASSERT_EQUALS("", 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 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 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 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(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 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 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 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 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 reference.\n", errout.str()); { // 8-byte data should be passed by 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 reference.\n", errout.str()); Settings s64(_settings); s64.platform(cppcheck::Platform::Unix64); check(code, &s64); 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("", 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" "}", 0, false, false, false); 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" "}"); 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); 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()); } 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" " 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;\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("", 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 |= 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("", 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, &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, &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, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() { xResAccess->exit(); }", nullptr, 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("void foo() {\n" " wxCHECK2(state < 3 && state >= 0, return);\n" " _checkboxState = state;\n" "}"); ASSERT_EQUALS("", 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){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" " __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, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " (beat < 100) ? exit(0) : (void)0;\n" " bar();\n" "}", nullptr, 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 (c == 1) 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) *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 x) {\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()); // #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 (*something)(void);\n" "};\n" "void something(void) {}\n" "void f() {\n" " struct callbacks ops = { .something = ops.something };\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()); } 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 "}",0,false,false,false); TODO_ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '+' and '?'.\n", "", errout.str()); // TODO: Is that really necessary, or is this pattern too unlikely? 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) Ineffective statement similar to '*A++;'. 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) Ineffective statement similar to '*A++;'. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Ineffective statement similar to '*A++;'. 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) Ineffective statement similar to '*A++;'. 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) Ineffective statement similar to '*A++;'. 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) Ineffective statement similar to '*A++;'. 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) Ineffective statement similar to '*A++;'. 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 duplicateExpression1() { check("void foo(int a) {\n" " if (a == a) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [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] -> [test.cpp:2]: (style) Same expression on both sides of '&&'.\n" "[test.cpp:3] -> [test.cpp:3]: (style) Same expression on both sides of '=='.\n" "[test.cpp:4] -> [test.cpp:4]: (style) Same expression on both sides of '>'.\n" "[test.cpp:5] -> [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] -> [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] -> [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] -> [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] -> [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] -> [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] -> [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] -> [test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo() {\n" " if (x!=2 || y!=3 || x!=2) {}\n" "}"); TODO_ASSERT_EQUALS("error", "", 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] -> [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] -> [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] -> [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] -> [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] -> [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] -> [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] -> [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] -> [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 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("", 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() {\n" " bool a = bar.isSet() && bar->isSet();\n" " bool b = bar.isSet() && bar.isSet();\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [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] -> [test.cpp:2]: (style) Same expression on both sides of '|'.\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] -> [test.cpp:2]: (style) Same expression on both sides of '>'.\n", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) 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] -> [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] -> [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] -> [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] -> [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] -> [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, &settings); ASSERT_EQUALS("[test.cpp:2] -> [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] -> [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] -> [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] -> [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] -> [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] -> [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 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("void f() {\n" " if( a ? (b ? false:false): false ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (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()); } 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 checkSignOfUnsignedVariable() { check( "void foo() {\n" " for(unsigned char i = 10; i >= 0; i--)" " printf(\"%u\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned variable '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--)" " printf(\"%u\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned variable 'i' can't be negative so it is unnecessary to test it.\n", errout.str()); check( "bool foo(unsigned int x) {\n" " if (x < 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x) {\n" " if (x < 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x) {\n" " if (0 > x)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x) {\n" " if (0 > x)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x) {\n" " if (x >= 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned variable 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check( "bool foo(int x) {\n" " if (x >= 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (x < 0 && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (x < 0 && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (0 > x && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (0 > x && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (x >= 0 && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned variable 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (x >= 0 && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (y && x < 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (y && x < 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (y && 0 > x)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (y && 0 > x)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (y && x >= 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned variable 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (y && x >= 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (x < 0 || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (x < 0 || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (0 > x || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned variable 'x' is less than zero.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (0 > x || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(unsigned int x, bool y) {\n" " if (x >= 0 || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned variable 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check( "bool foo(int x, bool y) {\n" " if (x >= 0 || y)" " return true;\n" " return false;\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("[test.cpp:2]: (style, inconclusive) Checking if unsigned variable 'x' is less than zero. This might be a false warning.\n", errout.str()); } } void checkSignOfPointer() { check( "bool foo(int* x) {\n" " if (x >= 0)" " bar();\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( "bool foo(int* x) {\n" " if (*x >= 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(int* x) {\n" " if (x < 0)" " bar();\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( "bool foo(int* x) {\n" " if (*x < 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(int* x, int* y) {\n" " if (x - y < 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(int* x, int* y) {\n" " if (x - y <= 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(int* x, int* y) {\n" " if (x - y > 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(int* x, int* y) {\n" " if (x - y >= 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x) {\n" " if (0 <= x)" " bar();\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( "bool foo(int* x) {\n" " if (0 <= x[0])" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x) {\n" " if (0 <= x.y)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x) {\n" " if (0 <= x->y)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x, Bar* y) {\n" " if (0 <= x->y - y->y )" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x) {\n" " if (0 > x)" " bar();\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( "bool foo(int* x) {\n" " if (0 > x[0])" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x) {\n" " if (0 > x.y)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "bool foo(Bar* x) {\n" " if (0 > x->y)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " int (*t)(void *a, void *b);\n" " if (t(a, b) < 0)\n" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " int (*t)(void *a, void *b);\n" " if (0 > t(a, b))\n" " bar();\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) Invalid memory address freed.\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) Invalid memory address freed.\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) Invalid memory address freed.\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) Invalid memory address freed.\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) Invalid memory address freed.\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) Invalid memory address freed.\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) Invalid memory address freed.\n", errout.str()); check( "void foo(size_t xx) {\n" " char *ptr; ptr = malloc(42);\n" " ptr += xx;\n" " free(ptr - xx - 1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Invalid memory address freed.\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("", 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" " 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" " 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("[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() { // Simple tests check("void f(int i) {\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); { // non-local variable => only show warning when inconclusive is used const char code[] = "int i;\n" "void f() {\n" " i = 1;\n" " i = 1;\n" "}"; check(code, "test.cpp", false, false); // inconclusive = false ASSERT_EQUALS("", errout.str()); check(code, "test.cpp", false, true); // inconclusive = true ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); } check("void f() {\n" " int i;\n" " i = 1;\n" " i = 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " static int i;\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); check("void f() {\n" " int i[10];\n" " i[2] = 1;\n" " i[2] = 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i[2]' is reassigned a value before the old one has been used.\n", errout.str()); check("void f(int x) {\n" " int i[10];\n" " i[x] = 1;\n" " x=1;\n" " i[x] = 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void f(const int x) {\n" " int i[10];\n" " i[x] = 1;\n" " i[x] = 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i[x]' is reassigned a value before the old one has been used.\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" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'bar' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", 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:2] -> [test.cpp:4]: (style) Variable 'i' is reassigned a value before the old one has been used.\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" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'i' is reassigned a value before the old one has been used.\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" "}", nullptr, false, false, false); 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:4] -> [test.cpp:5]: (style) Variable 'i' is reassigned a value before the old one has been used.\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:5] -> [test.cpp:6]: (style) Variable 'x' is reassigned a value before the old one has been used.\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()); // from #3103 (avoid a false negative) check("int foo(){\n" " int x;\n" " x = 1;\n" " x = 1;\n" " return x + 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'x' is reassigned a value before the old one has been used.\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()); check("void f() {\n" // Ticket #4356 " int x = 0;\n" // <- ignore assignment with 0 " x = 3;\n" "}", 0, false, false, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i = 54;\n" " i = 0;\n" "}", 0, false, false, false); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " int i = 54;\n" " i = 1;\n" "}", 0, false, false, false); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Variable 'i' is reassigned a value before the old one has been used.\n", 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:5] -> [test.cpp:6]: (style) Variable 'ab.a' is reassigned a value before the old one has been used.\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:4] -> [test.cpp:5]: (style) Variable 'p' is reassigned a value before the old one has been used.\n" "[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:3] -> [test.cpp:4]: (style, inconclusive) Variable 'memptr' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", 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, inconclusive) Variable 'aSrcBuf.mnBitCount' 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 redundantMemWrite() { // 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" " 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("voif 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("voif 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("voif f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" "} while (i != EOF)" "}"); ASSERT_EQUALS("", errout.str()); check("voif f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" "} while (EOF != i)" "}"); ASSERT_EQUALS("", errout.str()); // check fgetc check("voif 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("voif 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("voif f (FILE * pFile){\n" "signed char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("", errout.str()); check("voif f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" "} while (i != EOF)" "}"); ASSERT_EQUALS("", errout.str()); check("voif 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 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); TODO_ASSERT_EQUALS("", "[test.cpp:1] -> [test.cpp:1]: (style) Same expression on both sides of '||'.\n", 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()); } 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()); } 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 variable 'd.n' is less than zero.\n" "[test.c:12]: (style) Checking if unsigned variable '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 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 " )); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestOther) cppcheck-1.82/test/testpath.cpp000066400000000000000000000125401322667425100165550ustar00rootroot00000000000000/* * 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 "path.h" #include "testsuite.h" #include #include class TestPath : public TestFixture { public: TestPath() : TestFixture("TestPath") { } private: void run() { 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")); #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 { std::vector basePaths; basePaths.push_back(""); // Don't crash with empty paths basePaths.push_back("C:/foo"); basePaths.push_back("C:/bar/"); basePaths.push_back("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.82/test/testpathmatch.cpp000066400000000000000000000133401322667425100175710ustar00rootroot00000000000000/* * 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 "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() { 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; masks.push_back("src/"); masks.push_back("module/"); PathMatch match(masks); ASSERT(!match.match("project/")); } void twomasklongerpath2() const { std::vector masks; masks.push_back("src/"); masks.push_back("module/"); PathMatch match(masks); ASSERT(match.match("project/src/")); } void twomasklongerpath3() const { std::vector masks; masks.push_back("src/"); masks.push_back("module/"); PathMatch match(masks); ASSERT(match.match("project/module/")); } void twomasklongerpath4() const { std::vector masks; masks.push_back("src/"); masks.push_back("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.82/test/testpostfixoperator.cpp000066400000000000000000000274171322667425100211020ustar00rootroot00000000000000/* * 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 "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"); tokenizer.simplifyTokenList2(); // Check for postfix operators.. CheckPostfixOperator checkPostfixOperator(&tokenizer, &settings, this); checkPostfixOperator.postfixOperator(); } void run() { settings.addEnabled("performance"); TEST_CASE(testsimple); TEST_CASE(testfor); TEST_CASE(testvolatile); TEST_CASE(testiterator); TEST_CASE(test2168); TEST_CASE(pointer); // #2321 - postincrement of pointer is OK TEST_CASE(testtemplate); // #4686 TEST_CASE(testmember); TEST_CASE(testcomma); } 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()); } 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" "}"); TODO_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 pointer() { check("static struct class * ab;\n" "int * p;\n" "p++;\n"); ASSERT_EQUALS("", errout.str()); } void testtemplate() { check("bool foo() {\n" " std::vector::iterator aIter(aImport.begin());\n" " aIter++;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void testmember() { check("bool foo() {\n" " class A {}; class B {A a;};\n" " B b;\n" " b.a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("bool foo() {\n" " class A {}; class B {A a;};\n" " B b;\n" " foo(b.a++);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testcomma() { check("bool foo(int i) {\n" " class A {};\n" " A a;\n" " i++, a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("bool foo(int i) {\n" " class A {};\n" " A a;\n" " foo(i, a++);\n" " foo(a++, i);\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestPostfixOperator) cppcheck-1.82/test/testpreprocessor.cpp000066400000000000000000002560021322667425100203520ustar00rootroot00000000000000/* * 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 . */ // 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) { } class OurPreprocessor : public Preprocessor { public: static std::string expandMacros(const char code[], ErrorLogger *errorLogger = 0) { 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() { // 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(validateCfg); TEST_CASE(if_sizeof); TEST_CASE(invalid_ifs); // #5909 TEST_CASE(garbage); TEST_CASE(wrongPathOnErrorDirective); TEST_CASE(testDirectiveIncludeTypes); TEST_CASE(testDirectiveIncludeLocations); TEST_CASE(testDirectiveIncludeComments); TEST_CASE(testSameLine); // #7912 } 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 (std::set::const_iterator it = configs.begin(); it != configs.end(); ++it) { try { const std::string &cfgcode = preprocessor0.getcode(tokens, *it, files, std::string(code).find("#file") != std::string::npos); actual[*it] = cfgcode; } catch (...) { } } } std::string getConfigsStr(const char filedata[], const char *arg=NULL) { 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 (std::set::const_iterator it = configs.begin(); it != configs.end(); ++it) ret += *it + '\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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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, static_cast(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 validateCfg() { Preprocessor preprocessor(settings0, this); std::list macroUsageList; std::vector files(1, "test.c"); simplecpp::MacroUsage macroUsage(files); macroUsage.useLocation.fileIndex = 0; macroUsage.useLocation.line = 1; macroUsage.macroName = "X"; macroUsageList.push_back(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)); } 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()); } void testSameLine() { // Ticket #7912 const char code[] = "#line 1 \"bench/btl/libs/BLAS/blas_interface_impl.hh\" \n" "template < > class blas_interface < float > : public c_interface_base < float > \n" "{ } ;\n" "#line 1 \"bench/btl/libs/BLAS/blas_interface_impl.hh\" \n" "template < > class blas_interface < double > : public c_interface_base < double > \n" "{ } ;"; const char exp[] = "template < > class blas_interface < float > : public c_interface_base < float >\n" "{ } ; template < > class blas_interface < double > : public c_interface_base < double > { } ;"; Preprocessor preprocessor(settings0, this); ASSERT_EQUALS(exp, preprocessor.getcode(code, "", "test.cpp")); } }; REGISTER_TEST(TestPreprocessor) cppcheck-1.82/test/testrunner.cpp000066400000000000000000000033511322667425100171320ustar00rootroot00000000000000/* * 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 "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, const_cast(argv)); 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.82/test/testrunner.vcxproj000066400000000000000000000423741322667425100200530ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {c183db5b-ad6c-423d-80ca-1f9549555a1a} Create Create Create Create {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4} testrunner 8.1 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 Application Unicode false v141 ..\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 ..\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 ..\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 ..\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 cppcheck-1.82/test/testrunner.vcxproj.filters000066400000000000000000000174031322667425100215150ustar00rootroot00000000000000 {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 Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files cppcheck-1.82/test/testsamples.cpp000066400000000000000000000142611322667425100172670ustar00rootroot00000000000000/* * 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 "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() { 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) { CLEAR_REDIRECT_ERROUT; char* path = new char[i->first.size() + 1]; strcpy(path, i->first.c_str()); const char* 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.c_str()); 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); } delete[] path; } } class CppCheckExecutor2 : public CppCheckExecutor { public: void settings(const Settings &set) { setSettings(set); } }; void runConsoleCodePageTranslationOnWindows() const { REDIRECT; std::vector msgs; msgs.push_back("ASCII"); // first entry should be using only ASCII msgs.push_back("kääk"); msgs.push_back("Português"); // msgs.push_back("日本語"); // msgs.push_back("한국어"); // msgs.push_back("Русский"); // msgs.push_back("中文"); 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.82/test/testsimplifytemplate.cpp000066400000000000000000002147011322667425100212140ustar00rootroot00000000000000/* * 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 "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() { 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(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_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 TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateNamePosition); TEST_CASE(expandSpecialized); TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); TEST_CASE(templateAlias3); // #8315 // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatch); } std::string tok(const char code[], bool simplify = true, 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"); if (simplify) tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, !simplify); } std::string tok(const char code[], const char filename[]) { errout.str(""); settings.debugwarnings = false; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, false); } void template1() { const char code[] = "template void f(T val) { T a; }\n" "f(10);"; const char expected[] = "f ( 10 ) ; " "void f ( int val ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template2() { const char code[] = "template class Fred { T a; };\n" "Fred fred;"; const char expected[] = "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[] = "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[] = "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[] = "template < class T > Fred < T > :: Fred ( ) { } " // <- TODO: this should be removed "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[] = "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[] = "template < typename T > class ABC { public: } ; " "int main ( ) { " "std :: vector < int > v ; " "v . push_back ( 4 ) ; " "return 0 ; " "}"; const char current[] = "template < typename T > class ABC { public: } ; " "int main ( ) { " "ABC < int > :: type v ; " "v . push_back ( 4 ) ; " "return 0 ; " "}"; 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 ; } } ; " "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[] = "void f ( ) { A a ; } " "template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; " "class A { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template10() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " foo<3,int>();\n" "}\n"; // The expected result.. const char expected[] = "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[] = "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"; // The expected result.. const char expected[] = "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" "{\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()];"; // Just run it and check that there are not assertions. tok(code); } void template14() { const char code[] = "template <> void foo()\n" "{ x(); }\n" "\n" "int main()\n" "{\n" "foo();\n" "}\n"; // The expected result.. const char expected[] = "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> ( ) { } " "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[] = "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[] = "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"; // Assert that there is no segmentation fault.. tok(code); } void template18() { const char code[] = "template class foo { T a; };\n" "foo *f;"; const char expected[] = "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[] = "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[] = "template < class T > A < T > :: ~ A ( ) { } " // <- TODO: this should be removed "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[] = "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[] = "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[] = "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[] = "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[] = "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 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[] = "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 actual[] = "template < int n > struct B { int a [ n ] ; } ; " "bitset<1> z ; " "class bitset<1> : B < 4 > { } ;"; const char expected[] = "bitset<1> z ; " "class bitset<1> : B < 4 > { } ; " "struct B < 4 > { int a [ 4 ] ; } ;"; TODO_ASSERT_EQUALS(expected, actual, 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 ; } ; 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("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("A a ; struct A { } ;", tok(code)); // #7409 - rvalue const char code2[] = "template struct A{}; A a;"; ASSERT_EQUALS("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 { } ; " "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("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 "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);"; ASSERT_EQUALS("namespace abc { " "template < typename T > struct X { void f ( X < T > & x ) { } } ; " "} " "template < > int X < int > :: 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("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("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 { } ; 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 { } ; 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 { } ; 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"; tok(code); // Don't crash or freeze } 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; };"; ASSERT_EQUALS("struct A { template < typename T > struct X { T t ; } ; } ;", 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("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 x ( ) { return f ( 123 ) ; } int f ( int t ) { return t ; }", tok(code2)); } void template42() { // #4878 cpcheck 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" "}"; tok(code); } void template43() { // #5097 - Assert due to '>>' in 'B>' not being treated as end of template instantation const char code[] = "template struct C { };" "template struct D { static int f() { return C::f(); } };" "template inline int f2() { return D::f(); }" "template int f1(int x, T *) { int id = f2(); return id; }" "template <> struct C < B < A >> {" " static int f() {" " return f1 < B < A >> (0, reinterpret_cast< B *>(E::Int(-1)));" " }" "};"; tok(code); // Don't assert } void template44() { // #5297 tok("template struct StackContainer {" " void foo(int i) {" " if (0 >= 1 && i<0) {}" " }" "};" "template class ZContainer : public StackContainer {};" "struct FGSTensor {};" "class FoldedZContainer : public ZContainer {};"); } void template45() { // #5814 tok("namespace Constants { const int fourtytwo = 42; } " "template struct TypeMath { " " static const int mult = sizeof(T) * U; " "}; " "template struct FOO { " " enum { value = TypeMath::something }; " "};"); 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[] = "template < class T > void Fred < T > :: f ( ) { } " "template < class T > void Fred < T > :: g ( ) { } " "template void Fred :: f ( ) ; " "template void Fred :: g ( ) ; " "class Fred { void f ( ) ; void g ( ) ; } ; " "Fred :: f ( ) { } " "Fred :: g ( ) { } " "class Fred { void f ( ) ; void g ( ) ; } ; " "Fred :: f ( ) { } " "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::g() { }\n"; const char expected[] = "template < class T > class Fred { void f ( ) ; } ; " "template < class T > void Fred < T > :: f ( ) { } " "template < > void Fred < float > :: f ( ) { } " "template < > void Fred < int > :: g ( ) { }"; 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 tok("template int sum() { " " return value + sum(); " "} " "template int calculate_value() { " " return sum(); " "} " "int value = calculate_value<1,1>();"); } void template53() { // #4335 tok("template struct Factorial { " " enum { value = N * Factorial::value }; " "};" "template <> struct Factorial<0> { " " enum { value = 1 }; " "};" "const int x = Factorial<4>::value;", /*simplify=*/true, /*debugwarnings=*/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( "A a ( 0 ) ; struct A { " "A ( int * p ) { p ; } " "} ; " "struct A { " "A ( int * p ) { " "p ; " "} } ;", tok("template struct A\n" "{\n" " A(T* p) {\n" " (A*)(p);\n" " }\n" "};\n" "A a(0);")); } void template56() { // #7117 tok("template struct Foo { " " std::array mfoo; " "}; " "void foo() { " " Foo myFoo; " "}", /*simplify=*/true, /*debugwarnings=*/true); ASSERT_EQUALS("", errout.str()); } void template57() { // #7891 const char code[] = "template struct Test { Test(T); };\n" "Test test( 0 );"; const char exp [] = "Test test ( 0 ) ; " "struct Test { Test ( 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 foo ( ) {" " TestArithmetic ( ) ; " "} " "void TestArithmetic ( ) {" " x ( 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> { enum FacHelper { value = 1 } ; } ; " "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 = 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[] = "template < typename T > void f ( ) { } " // <- TODO: This template is not expanded "void j ( ) { h ( ) ; } " "void h ( ) { f < S :: type ( 0 ) > ( ) ; } " "struct S { } ;"; ASSERT_EQUALS(exp, 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[] = "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[] = "template < class T > void f ( ) { x = y ? C1 < int > :: allocate ( 1 ) : 0 ; } " "template < class T , int S > C3 < T , S > :: C3 ( const C3 < T , S > & v ) { C1 < T * > c1 ; } " "C3 c3 ; " "class C3 { } ; " "C3 :: C3 ( const C3 & v ) { C1 c1 ; } " "struct C1 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" "template struct S> {b};\n" "S s;"; const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S < C < T > > { b } ; S s ; struct S { a } ;"; ASSERT_EQUALS(exp, tok(code)); } void template_specialization_2() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" "template struct S> {b};\n" "S> s;"; const char exp[] = "template < typename T > struct C { } ; template < typename T > struct S { a } ; S> s ; struct S> { b } ;"; ASSERT_EQUALS(exp, tok(code)); } void template_enum() { const char code1[] = "template \n" "struct Unconst {\n" " typedef T type;\n" "};\n" "template \n" "struct Unconst {\n" " typedef T type;\n" "};\n" "template \n" "struct Unconst {\n" " typedef T& type;\n" "};\n" "template \n" "struct Unconst {\n" " typedef T* type;\n" "};\n" "template \n" "struct type_equal {\n" " enum { value = 0 };\n" "};\n" "template \n" "struct type_equal {\n" " enum { value = 1 };\n" "};\n" "template\n" "struct template_is_const\n" "{\n" " enum {value = !type_equal::type>::value };\n" "};"; const char exp1[] = "template < class T > struct Unconst { } ; " "template < class T > struct Unconst < const T > { } ; " "template < class T > struct Unconst < const T & > { } ; " "template < class T > struct Unconst < T * const > { } ; " "template < class T1 , class T2 > struct type_equal { enum Anonymous0 { value = 0 } ; } ; " "template < class T > struct type_equal < T , T > { enum Anonymous1 { value = 1 } ; } ; " "template < class T > struct template_is_const { enum Anonymous2 { value = ! type_equal < T , Unconst < T > :: type > :: value } ; } ;"; ASSERT_EQUALS(exp1, tok(code1)); } void template_default_parameter() { { const char code[] = "template \n" "class A\n" "{ T ar[n]; };\n" "\n" "void f()\n" "{\n" " A a1;\n" " A a2;\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " A a1 ;" " A a2 ; " "} " "class A " "{ int ar [ 2 ] ; } ; " "class A " "{ int ar [ 3 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template \n" "class A\n" "{ T ar[n1+n2]; };\n" "\n" "void f()\n" "{\n" " A a1;\n" " A a2;\n" "}\n"; // The expected result.. const char expected[] = "void f ( ) " "{" " A a1 ;" " A a2 ; " "} " "class A " "{ int ar [ 5 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template \n" "class A\n" "{ T ar[n]; };\n" "\n" "void f()\n" "{\n" " A a1;\n" " A a2;\n" "}\n"; const char wanted[] = "template < class T , int n >" " class A" " { T ar [ n ] ; } ;" " void f ( )" " {" " A a1 ;" " A a2 ;" " }" " class A" " { int ar [ 2 ] ; }" " class A" " { int ar [ 3 ] ; }"; const char current[] = "void f ( ) " "{ " "A < int , ( int ) 2 > a1 ; " "A a2 ; " "} " "class A " "{ int ar [ 3 ] ; } ;"; TODO_ASSERT_EQUALS(wanted, current, tok(code)); } { const char code[] = "class A { }; " "template class B { }; " "template> class C { }; " "template> class D { };"; ASSERT_EQUALS("class A { } ; " "template < class T > class B { } ; " "template < class T1 , class T2 = B < T1 > > class C { } ; " "template < class T1 = A , typename T2 = B < A > > class D { } ;", tok(code)); } { // #7548 const char code[] = "template class DefaultMemory {}; " "template > class thv_table_c {}; " "thv_table_c id_table_m;"; const char exp [] = "template < class T , class U > class DefaultMemory { } ; " "thv_table_c> id_table_m ; " "class thv_table_c> { } ;"; const char curr[] = "template < class T , class U > class DefaultMemory { } ; " "thv_table_c> id_table_m ; " "class thv_table_c> { } ;"; TODO_ASSERT_EQUALS(exp, curr, tok(code)); } } void template_default_type() { const char code[] = "template \n" "class A\n" "{\n" "public:\n" " void foo() {\n" " int a;\n" " a = static_cast(a);\n" " }\n" "};\n" "\n" "template \n" "class B\n" "{\n" "protected:\n" " A a;\n" "};\n" "\n" "class C\n" " : public B\n" "{\n" "};\n"; tok(code); //ASSERT_EQUALS("[file1.cpp:15]: (error) Internal error: failed to instantiate template. The checking continues anyway.\n", errout.str()); ASSERT_EQUALS("", errout.str()); } void template_typename() { { const char code[] = "template \n" "void foo(typename T::t *)\n" "{ }"; // The expected result.. const char expected[] = "template < class T > void foo ( T :: t * ) { }"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f() {\n" " x(sizeof typename);\n" " type = 0;\n" "}"; ASSERT_EQUALS("void f ( ) { x ( sizeof ( typename ) ) ; type = 0 ; }", tok(code)); } } void template_constructor() { // #3152 - if template constructor is removed then there might be // "no constructor" false positives const char code[] = "class Fred {\n" " template explicit Fred(T t) { }\n" "}"; ASSERT_EQUALS("class Fred { template < class T > explicit Fred ( T t ) { } }", tok(code)); // #3532 const char code2[] = "class Fred {\n" " template Fred(T t) { }\n" "}"; ASSERT_EQUALS("class Fred { template < class T > Fred ( T t ) { } }", tok(code2)); } void syntax_error_templates_1() { // ok code.. using ">" for a comparison tok("xz> xyz;\n"); ASSERT_EQUALS("", errout.str()); // ok code tok("template operator<(T a, T b) { }\n"); ASSERT_EQUALS("", errout.str()); // ok code (ticket #1984) tok("void f(a) int a;\n" "{ ;x" ASSERT_THROW(tok("x xyz;\n"), InternalError); // bad code ASSERT_THROW(tok("typedef\n" " typename boost::mpl::if_c<\n" " _visitableIndex < boost::mpl::size< typename _Visitables::ConcreteVisitables >::value\n" " , ConcreteVisitable\n" " , Dummy< _visitableIndex >\n" " >::type ConcreteVisitableOrDummy;\n"), InternalError); // code is ok, don't show syntax error tok("struct A {int a;int b};\n" "class Fred {" "public:\n" " Fred() : a({1,2}) {\n" " for (int i=0;i<6;i++);\n" // <- no syntax error " }\n" "private:\n" " A a;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void template_member_ptr() { // Ticket #5786 tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tok("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); } void template_namespace_1() { // #6570 const char code[] = "namespace {\n" " template void Fred(T value) { }\n" "}\n" "Fred(123);"; ASSERT_EQUALS("namespace { } " "Fred ( 123 ) ; " "void Fred ( int value ) { }", tok(code)); } void template_namespace_2() { // #8283 const char code[] = "namespace X {\n" " template struct S { };\n" "}\n" "X::S s;"; ASSERT_EQUALS("X::S s ; " "struct X::S { } ;", tok(code)); } void template_namespace_3() { const char code[] = "namespace test16 {\n" " template struct foo {\n" " static void *bar();\n" " };\n" " void *test() { return foo::bar(); }\n" "}"; ASSERT_EQUALS("namespace test16 {" " void * test ( ) {" " return test16::foo :: bar ( ) ;" " } " "} " "struct test16::foo {" " static void * bar ( ) ; " "} ;", tok(code)); } void template_namespace_4() { const char code[] = "namespace foo {\n" " template class A { void dostuff() {} };\n" " struct S : public A {\n" " void f() {\n" " A::dostuff();\n" " }\n" " };\n" "}"; ASSERT_EQUALS("namespace foo {" " struct S : public foo::A {" " void f ( ) {" " foo::A :: dostuff ( ) ;" " }" " } ; " "} " "class foo::A { void dostuff ( ) { } } ;", tok(code)); } void template_namespace_5() { const char code[] = "template struct S {};\n" "namespace X { S s; }"; ASSERT_EQUALS("namespace X { S s ; } struct S { } ;", tok(code)); } unsigned int templateParameters(const char code[]) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); return TemplateSimplifier::templateParameters(tokenizer.tokens()->next()); } void templateParameters() { // Test that the function TemplateSimplifier::templateParameters works ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(0U, templateParameters("X>x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(0U, templateParameters("X<...> x;")); ASSERT_EQUALS(0U, templateParameters("X x;")); // Invalid syntax ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(0U, templateParameters("X x;")); // Invalid syntax ASSERT_EQUALS(2U, templateParameters("X x;")); ASSERT_EQUALS(2U, templateParameters("X x;")); ASSERT_EQUALS(3U, templateParameters("X x;")); TODO_ASSERT_EQUALS(1U, 0U, templateParameters("X x;")); // Mishandled valid syntax TODO_ASSERT_EQUALS(2U, 0U, templateParameters("X x;")); // Mishandled valid syntax ASSERT_EQUALS(2U, templateParameters("X<1, T> x;")); ASSERT_EQUALS(1U, templateParameters("X x;")); ASSERT_EQUALS(2U, templateParameters("X=0> x;")); ASSERT_EQUALS(3U, templateParameters("X=0, i - 2> x;")); } // Helper function to unit test TemplateSimplifier::getTemplateNamePosition int templateNamePositionHelper(const char code[], unsigned offset = 0, bool onlyCreateTokens = false) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (onlyCreateTokens) tokenizer.createTokens(istr, "test.cpp"); else tokenizer.tokenize(istr, "test.cpp", emptyString); const Token *_tok = tokenizer.tokens(); for (unsigned i = 0 ; i < offset ; ++i) _tok = _tok->next(); return TemplateSimplifier::getTemplateNamePosition(_tok); } void templateNamePosition() { // Template class ASSERT_EQUALS(2, templateNamePositionHelper("template class A {};", 4)); ASSERT_EQUALS(2, templateNamePositionHelper("template struct A {};", 4)); ASSERT_EQUALS(2, templateNamePositionHelper("template class A : B {};", 4)); ASSERT_EQUALS(2, templateNamePositionHelper("template struct A : B {};", 4)); // Template function definitions ASSERT_EQUALS(2, templateNamePositionHelper("template unsigned foo() { return 0; }", 4)); ASSERT_EQUALS(3, templateNamePositionHelper("template unsigned* foo() { return 0; }", 4)); ASSERT_EQUALS(3, templateNamePositionHelper("template const unsigned foo() { return 0; }", 4)); ASSERT_EQUALS(4, templateNamePositionHelper("template const unsigned& foo() { return 0; }", 4)); // Class template members ASSERT_EQUALS(4, templateNamePositionHelper("class A { template unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 19)); ASSERT_EQUALS(5, templateNamePositionHelper("class A { template const unsigned foo(); }; " "template const unsigned A::foo() { return 0; }", 20)); TODO_ASSERT_EQUALS(7, -1, templateNamePositionHelper("class A { class B { template const unsigned foo(); }; } ; " "template const unsigned A::B::foo() { return 0; }", 25)); // Template class member ASSERT_EQUALS(6, templateNamePositionHelper("template class A { A(); }; " "template A::A() {}", 18)); ASSERT_EQUALS(8, templateNamePositionHelper("template class A { A(); }; " "template A::A() {}", 24)); ASSERT_EQUALS(7, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 19)); ASSERT_EQUALS(9, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 25)); ASSERT_EQUALS(9, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 25, /*onlyCreateTokens=*/true)); ASSERT_EQUALS(12, templateNamePositionHelper("template class v {}; " "template class A { unsigned foo(); }; " "template<> unsigned A >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true)); } void expandSpecialized() { ASSERT_EQUALS("class A { } ;", tok("template<> class A {};")); ASSERT_EQUALS("class A : public B { } ;", tok("template<> class A : public B {};")); } void templateAlias1() { const char code[] = "template struct Foo {};\n" "template using Bar = Foo;\n" "Bar b;\n"; const char expected[] = "; Foo b ; struct Foo { } ;"; ASSERT_EQUALS(expected, tok(code)); } void templateAlias2() { const char code[] = "namespace A { template struct Foo {}; }\n" "template using Bar = A::Foo;\n" "Bar b;\n"; const char expected[] = "; A::Foo b ; struct A::Foo { } ;"; ASSERT_EQUALS(expected, tok(code)); } void templateAlias3() { // #8315 const char code[] = "template struct Tag {};\n" "template using SPtr = std::shared_ptr)>;\n" "SPtr<0> s;"; const char expected[] = "; std :: shared_ptr < void ( Tag<0> ) > s ; struct Tag<0> { } ;"; ASSERT_EQUALS(expected, tok(code)); } unsigned int instantiateMatch(const char code[], const std::size_t numberOfArguments, const char patternAfter[]) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); return TemplateSimplifier::instantiateMatch(tokenizer.tokens(), numberOfArguments, patternAfter); } void instantiateMatch() { // Ticket #8175 ASSERT_EQUALS(false, instantiateMatch("ConvertHelper < From, To > c ;", 2, ":: %name% (")); ASSERT_EQUALS(true, instantiateMatch("ConvertHelper < From, To > :: Create ( ) ;", 2, ":: %name% (")); ASSERT_EQUALS(false, instantiateMatch("integral_constant < bool, sizeof ( ConvertHelper < From, To > :: Create ( ) ) > ;", 2, ":: %name% (")); ASSERT_EQUALS(false, instantiateMatch("integral_constant < bool, sizeof ( ns :: ConvertHelper < From, To > :: Create ( ) ) > ;", 2, ":: %name% (")); } }; REGISTER_TEST(TestSimplifyTemplate) cppcheck-1.82/test/testsimplifytokens.cpp000066400000000000000000004261151322667425100207100ustar00rootroot00000000000000/* * 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 "platform.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include class TestSimplifyTokens : public TestFixture { public: TestSimplifyTokens() : TestFixture("TestSimplifyTokens") { } private: Settings settings0; Settings settings1; Settings settings_std; Settings settings_windows; void run() { LOAD_LIB_2(settings_std.library, "std.cfg"); LOAD_LIB_2(settings_windows.library, "windows.cfg"); settings0.addEnabled("portability"); settings1.addEnabled("style"); settings_windows.addEnabled("portability"); // Make sure the Tokenizer::simplifyTokenList works. // The order of the simplifications is important. So this test // case shall make sure the simplifications are done in the // correct order TEST_CASE(simplifyTokenList1); // foo(p = new char[10]); => p = new char[10]; foo(p); TEST_CASE(simplifyAssignmentInFunctionCall); // ";a+=b;" => ";a=a+b;" TEST_CASE(simplifyCompoundAssignment); TEST_CASE(cast); TEST_CASE(iftruefalse); TEST_CASE(combine_strings); TEST_CASE(double_plus); TEST_CASE(redundant_plus); TEST_CASE(redundant_plus_numbers); TEST_CASE(parentheses1); TEST_CASE(parenthesesVar); // Remove redundant parentheses around variable .. "( %name% )" TEST_CASE(declareVar); TEST_CASE(declareArray); TEST_CASE(dontRemoveIncrement); TEST_CASE(removePostIncrement); TEST_CASE(removePreIncrement); TEST_CASE(elseif1); TEST_CASE(sizeof_array); TEST_CASE(sizeof5); TEST_CASE(sizeof6); TEST_CASE(sizeof7); TEST_CASE(sizeof8); TEST_CASE(sizeof9); TEST_CASE(sizeof10); TEST_CASE(sizeof11); TEST_CASE(sizeof12); TEST_CASE(sizeof13); TEST_CASE(sizeof14); TEST_CASE(sizeof15); TEST_CASE(sizeof16); TEST_CASE(sizeof17); TEST_CASE(sizeof18); TEST_CASE(sizeof19); // #1891 - sizeof 'x' TEST_CASE(sizeof20); // #2024 - sizeof a) TEST_CASE(sizeof21); // #2232 - sizeof...(Args) TEST_CASE(sizeof22); TEST_CASE(sizeofsizeof); TEST_CASE(casting); TEST_CASE(strlen1); TEST_CASE(strlen2); TEST_CASE(namespaces); // Assignment in condition.. TEST_CASE(ifassign1); TEST_CASE(ifAssignWithCast); TEST_CASE(whileAssign1); TEST_CASE(whileAssign2); TEST_CASE(whileAssign3); // varid TEST_CASE(whileAssign4); // links TEST_CASE(doWhileAssign); // varid TEST_CASE(test_4881); // similar to doWhileAssign (#4911), taken from #4881 with full code TEST_CASE(combine_wstrings); TEST_CASE(combine_ustrings); // Simplify "not" to "!" (#345) TEST_CASE(not1); // Simplify "and" to "&&" (#620) TEST_CASE(and1); // Simplify "or" to "||" TEST_CASE(or1); TEST_CASE(cAlternativeTokens); TEST_CASE(comma_keyword); TEST_CASE(remove_comma); // Simplify "?:" TEST_CASE(simplifyConditionOperator); // Simplify calculations TEST_CASE(calculations); TEST_CASE(comparisons); //remove dead code after flow control statements TEST_CASE(simplifyFlowControl); TEST_CASE(flowControl); // Simplify nested strcat() calls TEST_CASE(strcat1); TEST_CASE(strcat2); TEST_CASE(simplifyAtol) TEST_CASE(simplifyOperator1); TEST_CASE(simplifyOperator2); TEST_CASE(simplifyArrayAccessSyntax) TEST_CASE(simplify_numeric_condition) TEST_CASE(simplify_condition); TEST_CASE(pointeralias1); TEST_CASE(pointeralias2); TEST_CASE(pointeralias3); TEST_CASE(pointeralias4); // simplify "while (0)" TEST_CASE(while0); // ticket #3140 TEST_CASE(while0for); TEST_CASE(while1); TEST_CASE(duplicateDefinition); // ticket #3565 // remove "std::" on some standard functions TEST_CASE(removestd); // Tokenizer::simplifyInitVar TEST_CASE(simplifyInitVar); // Tokenizer::simplifyReference TEST_CASE(simplifyReference); // x = realloc(y,0); => free(y);x=0; TEST_CASE(simplifyRealloc); // while(f() && errno==EINTR) { } => while (f()) { } TEST_CASE(simplifyErrNoInWhile); // while(fclose(f)); => r = fclose(f); while(r){r=fclose(f);} TEST_CASE(simplifyFuncInWhile); // struct ABC { } abc; => struct ABC { }; ABC abc; TEST_CASE(simplifyStructDecl1); TEST_CASE(simplifyStructDecl2); // ticket #2579 TEST_CASE(simplifyStructDecl3); TEST_CASE(simplifyStructDecl4); TEST_CASE(simplifyStructDecl6); // ticket #3732 TEST_CASE(simplifyStructDecl7); // ticket #476 (static anonymous struct array) TEST_CASE(simplifyStructDecl8); // ticket #7698 // register int var; => int var; // inline int foo() {} => int foo() {} TEST_CASE(removeUnwantedKeywords); // remove calling convention __cdecl, __stdcall, ... TEST_CASE(simplifyCallingConvention); TEST_CASE(simplifyFunctorCall); TEST_CASE(simplifyFunctionPointer); // ticket #5339 (simplify function pointer after comma) TEST_CASE(redundant_semicolon); TEST_CASE(simplifyFunctionReturn); // void foo(void) -> void foo() TEST_CASE(removeVoidFromFunction); TEST_CASE(return_strncat); // ticket # 2860 Returning value of strncat() reported as memory leak // #3069 : for loop with 1 iteration // for (x=0;x<1;x++) { .. } // The for is redundant TEST_CASE(removeRedundantFor); TEST_CASE(consecutiveBraces); TEST_CASE(undefinedSizeArray); TEST_CASE(simplifyArrayAddress); // Replace "&str[num]" => "(str + num)" TEST_CASE(simplifyCharAt); TEST_CASE(simplifyOverride); // ticket #5069 } std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native) { errout.str(""); settings0.platform(type); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); if (simplify) tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, !simplify); } std::string tokWithWindows(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native) { errout.str(""); settings_windows.platform(type); Tokenizer tokenizer(&settings_windows, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); if (simplify) tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, !simplify); } std::string tok(const char code[], const char filename[], bool simplify = true) { errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, false); } std::string tokWithStdLib(const char code[]) { errout.str(""); Tokenizer tokenizer(&settings_std, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, false); } std::string tokenizeDebugListing(const char code[], bool simplify = false, const char filename[] = "test.cpp") { errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); // result.. return tokenizer.tokens()->stringifyList(true); } void simplifyTokenList1() { // #1717 : The simplifyErrNoInWhile needs to be used before simplifyIfAndWhileAssign.. ASSERT_EQUALS("; x = f ( ) ; while ( x == -1 ) { x = f ( ) ; }", tok(";while((x=f())==-1 && errno==EINTR){}",true)); } void simplifyAssignmentInFunctionCall() { ASSERT_EQUALS("; x = g ( ) ; f ( x ) ;", tok(";f(x=g());")); ASSERT_EQUALS("; hs = ( xyz_t ) { h . centerX , h . centerY , 1 + index } ; putInput ( hs , 1 ) ;", tok(";putInput(hs = (xyz_t) { h->centerX, h->centerY, 1 + index }, 1);")); } void simplifyCompoundAssignment() { ASSERT_EQUALS("; x = x + y ;", tok("; x += y;")); ASSERT_EQUALS("; x = x - y ;", tok("; x -= y;")); ASSERT_EQUALS("; x = x * y ;", tok("; x *= y;")); ASSERT_EQUALS("; x = x / y ;", tok("; x /= y;")); ASSERT_EQUALS("; x = x % y ;", tok("; x %= y;")); ASSERT_EQUALS("; x = x & y ;", tok("; x &= y;")); ASSERT_EQUALS("; x = x | y ;", tok("; x |= y;")); ASSERT_EQUALS("; x = x ^ y ;", tok("; x ^= y;")); ASSERT_EQUALS("; x = x << y ;", tok("; x <<= y;")); ASSERT_EQUALS("; x = x >> y ;", tok("; x >>= y;")); ASSERT_EQUALS("{ x = x + y ; }", tok("{ x += y;}")); ASSERT_EQUALS("{ x = x - y ; }", tok("{ x -= y;}")); ASSERT_EQUALS("{ x = x * y ; }", tok("{ x *= y;}")); ASSERT_EQUALS("{ x = x / y ; }", tok("{ x /= y;}")); ASSERT_EQUALS("{ x = x % y ; }", tok("{ x %= y;}")); ASSERT_EQUALS("{ x = x & y ; }", tok("{ x &= y;}")); ASSERT_EQUALS("{ x = x | y ; }", tok("{ x |= y;}")); ASSERT_EQUALS("{ x = x ^ y ; }", tok("{ x ^= y;}")); ASSERT_EQUALS("{ x = x << y ; }", tok("{ x <<= y;}")); ASSERT_EQUALS("{ x = x >> y ; }", tok("{ x >>= y;}")); ASSERT_EQUALS("; * p = * p + y ;", tok("; *p += y;")); ASSERT_EQUALS("; ( * p ) = ( * p ) + y ;", tok("; (*p) += y;")); ASSERT_EQUALS("; * ( p [ 0 ] ) = * ( p [ 0 ] ) + y ;", tok("; *(p[0]) += y;")); ASSERT_EQUALS("; p [ { 1 , 2 } ] = p [ { 1 , 2 } ] + y ;", tok("; p[{1,2}] += y;")); ASSERT_EQUALS("void foo ( ) { switch ( n ) { case 0 : ; x = x + y ; break ; } }", tok("void foo() { switch (n) { case 0: x += y; break; } }")); ASSERT_EQUALS("; x . y = x . y + 1 ;", tok("; x.y += 1;")); ASSERT_EQUALS("; x [ 0 ] = x [ 0 ] + 1 ;", tok("; x[0] += 1;")); ASSERT_EQUALS("; x [ y - 1 ] = x [ y - 1 ] + 1 ;", tok("; x[y-1] += 1;")); ASSERT_EQUALS("; x [ y ] = x [ y ++ ] + 1 ;", tok("; x[y++] += 1;")); ASSERT_EQUALS("; x [ ++ y ] = x [ y ] + 1 ;", tok("; x[++y] += 1;")); ASSERT_EQUALS(";", tok(";x += 0;")); TODO_ASSERT_EQUALS(";", "; x = x + '\\0' ;", tok("; x += '\\0'; ")); ASSERT_EQUALS(";", tok(";x -= 0;")); ASSERT_EQUALS(";", tok(";x |= 0;")); ASSERT_EQUALS(";", tok(";x *= 1;")); ASSERT_EQUALS(";", tok(";x /= 1;")); ASSERT_EQUALS("; a . x ( ) = a . x ( ) + 1 ;", tok("; a.x() += 1;")); ASSERT_EQUALS("; x ( 1 ) = x ( 1 ) + 1 ;", tok("; x(1) += 1;")); // #2368 ASSERT_EQUALS("{ j = j - i ; }", tok("if (false) {} else { j -= i; }")); // #2714 - wrong simplification of "a += b?c:d;" ASSERT_EQUALS("; a = a + ( b ? c : d ) ;", tok("; a+=b?c:d;")); ASSERT_EQUALS("; a = a * ( b + 1 ) ;", tok("; a*=b+1;")); ASSERT_EQUALS("; a = a + ( b && c ) ;", tok("; a+=b&&c;")); ASSERT_EQUALS("; a = a * ( b || c ) ;", tok("; a*=b||c;")); ASSERT_EQUALS("; a = a | ( b == c ) ;", tok("; a|=b==c;")); // #3469 ASSERT_EQUALS("; a = a + ( b = 1 ) ;", tok("; a += b = 1;")); // #7571 ASSERT_EQUALS("; foo = foo + [ & ] ( ) { } ;", tok("; foo += [&]() {int i;};")); } void cast() { ASSERT_EQUALS("if ( p == 0 ) { ; }", tok("if (p == (char *)0);")); ASSERT_EQUALS("return str ;", tok("return (char *)str;")); ASSERT_EQUALS("if ( * a )", tok("if ((char)*a)")); ASSERT_EQUALS("if ( & a )", tok("if ((int)&a)")); ASSERT_EQUALS("if ( * a )", tok("if ((unsigned int)(unsigned char)*a)")); ASSERT_EQUALS("class A { A operator* ( int ) ; } ;", tok("class A { A operator *(int); };")); ASSERT_EQUALS("class A { A operator* ( int ) const ; } ;", tok("class A { A operator *(int) const; };")); ASSERT_EQUALS("if ( p == 0 ) { ; }", tok("if (p == (char *)(char *)0);")); ASSERT_EQUALS("if ( p == 0 ) { ; }", tok("if (p == (char **)0);")); // no simplification as the cast may be important here. see #2897 for example ASSERT_EQUALS("; * ( ( char * ) p + 1 ) = 0 ;", tok("; *((char *)p + 1) = 0;")); ASSERT_EQUALS("if ( true )", tok("if ((unsigned char)1)")); // #4164 ASSERT_EQUALS("f ( 200 )", tok("f((unsigned char)200)")); ASSERT_EQUALS("f ( ( char ) 1234 )", tok("f((char)1234)")); // don't simplify downcast } void iftruefalse() { { const char code1[] = " void f() { int a; bool use = false; if( use ) { a=0; } else {a=1;} }"; const char code2[] = " void f() { int a; bool use = false; {a=1;} }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; bool use = true; if( use ) { a=0; } else {a=1;} }"; const char code2[] = " void f() { int a; bool use = true; { a=0; } }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; int use = 5; if( use ) { a=0; } else {a=1;} }"; const char code2[] = " void f() { int a; int use = 5; { a=0; } }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; int use = 0; if( use ) { a=0; } else {a=1;} }"; const char code2[] = " void f() { int a; int use = 0; {a=1;} }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; bool use = false; if( use ) a=0; else a=1; int c=1; }"; const char code2[] = " void f() { int a; bool use = false; { a=1; } int c=1; }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; bool use = true; if( use ) a=0; else a=1; int c=1; }"; const char code2[] = " void f() { int a; bool use = true; { a=0; } int c=1; }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; bool use = false; if( use ) a=0; else if( bb ) a=1; int c=1; }"; const char code2[] = " void f ( ) { int a ; bool use ; use = false ; { if ( bb ) { a = 1 ; } } int c ; c = 1 ; }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { int a; bool use = true; if( use ) a=0; else if( bb ) a=1; int c=1; }"; const char code2[] = " void f() { int a; bool use = true; { a=0;} int c=1; }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = "void f() { int a; bool use = true; if( use ) a=0; else if( bb ) a=1; else if( cc ) a=33; else { gg = 0; } int c=1; }"; const char code2[] = "void f ( ) { }"; ASSERT_EQUALS(code2, tok(code1)); } { const char code1[] = " void f() { if( aa ) { a=0; } else if( true ) a=1; else { a=2; } }"; const char code2[] = " void f ( ) { if ( aa ) { a = 0 ; } else { { a = 1 ; } } }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = " void f() { if( aa ) { a=0; } else if( false ) a=1; else { a=2; } }"; const char code2[] = " void f ( ) { if ( aa ) { a = 0 ; } else { { a = 2 ; } } }"; ASSERT_EQUALS(tok(code2), tok(code1)); } { const char code1[] = "static const int x=1; void f() { if(x) { a=0; } }"; ASSERT_EQUALS("void f ( ) { a = 0 ; }", tok(code1)); } } void combine_strings() { const char code1[] = "void foo()\n" "{\n" "const char *a =\n" "{\n" "\"hello \"\n" "\"world\"\n" "};\n" "}\n"; const char code2[] = "void foo()\n" "{\n" "const char *a =\n" "{\n" "\"hello world\"\n" "};\n" "}\n"; ASSERT_EQUALS(tok(code2), tok(code1)); } void combine_wstrings() { const char code[] = "a = L\"hello \" L\"world\" ;\n"; const char expected[] = "a = \"hello world\" ;"; Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(0, false)); ASSERT_EQUALS(true, tokenizer.tokens()->tokAt(2)->isLong()); } void combine_ustrings() { const char code[] = "abc = u\"abc\";"; const char expected[] = "abc = \"abc\" ;"; Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(0, false)); ASSERT_EQUALS(true, tokenizer.tokens()->tokAt(2)->isLong()); } void double_plus() { { const char code1[] = "void foo( int a )\n" "{\n" "a++;\n" "a--;\n" "++a;\n" "--a;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a ++ ; a -- ; ++ a ; -- a ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a+a;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a + a ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a+++b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a ++ + b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a---b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a -- - b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a--+b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a -- + b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a++-b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a ++ - b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a+--b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a + -- b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a-++b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a - ++ b ; }", tok(code1)); } } void redundant_plus() { { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a + + b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a + b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a + + + b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a + b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a + - b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a - b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a - + b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a - b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a - - b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a + b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a - + - b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a + b ; }", tok(code1)); } { const char code1[] = "void foo( int a, int b )\n" "{\n" "a=a - - - b;\n" "}\n"; ASSERT_EQUALS("void foo ( int a , int b ) { a = a - b ; }", tok(code1)); } } void redundant_plus_numbers() { { const char code1[] = "void foo( int a )\n" "{\n" "a=a + + 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a + 1 ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a + + + 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a + 1 ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a + - 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a - 1 ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a - + 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a - 1 ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a - - 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a + 1 ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a - + - 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a + 1 ; }", tok(code1)); } { const char code1[] = "void foo( int a )\n" "{\n" "a=a - - - 1;\n" "}\n"; ASSERT_EQUALS("void foo ( int a ) { a = a - 1 ; }", tok(code1)); } } void parentheses1() { ASSERT_EQUALS("a <= 110 ;", tok("a <= (10+100);")); ASSERT_EQUALS("while ( x ( ) == -1 ) { }", tok("while((x()) == -1){ }")); } void parenthesesVar() { // remove parentheses.. ASSERT_EQUALS("a = p ;", tok("a = (p);")); ASSERT_EQUALS("if ( a < p ) { }", tok("if(a<(p)){}")); ASSERT_EQUALS("void f ( ) { int p ; if ( p == -1 ) { } }", tok("void f(){int p; if((p)==-1){}}")); ASSERT_EQUALS("void f ( ) { int p ; if ( -1 == p ) { } }", tok("void f(){int p; if(-1==(p)){}}")); ASSERT_EQUALS("void f ( ) { int p ; if ( p ) { } }", tok("void f(){int p; if((p)){}}")); ASSERT_EQUALS("return p ;", tok("return (p);")); ASSERT_EQUALS("void f ( ) { int * p ; if ( * p == 0 ) { } }", tok("void f(){int *p; if (*(p) == 0) {}}")); ASSERT_EQUALS("void f ( ) { int * p ; if ( * p == 0 ) { } }", tok("void f(){int *p; if (*p == 0) {}}")); ASSERT_EQUALS("void f ( int & p ) { p = 1 ; }", tok("void f(int &p) {(p) = 1;}")); ASSERT_EQUALS("void f ( ) { int p [ 10 ] ; p [ 0 ] = 1 ; }", tok("void f(){int p[10]; (p)[0] = 1;}")); ASSERT_EQUALS("void f ( ) { int p ; if ( p == 0 ) { } }", tok("void f(){int p; if ((p) == 0) {}}")); ASSERT_EQUALS("void f ( ) { int * p ; * p = 1 ; }", tok("void f(){int *p; *(p) = 1;}")); ASSERT_EQUALS("void f ( ) { int p ; if ( p ) { } p = 1 ; }", tok("void f(){int p; if ( p ) { } (p) = 1;}")); ASSERT_EQUALS("void f ( ) { a . b ; }", tok("void f ( ) { ( & a ) -> b ; }")); // Ticket #5776 // keep parentheses.. ASSERT_EQUALS("b = a ;", tok("b = (char)a;")); ASSERT_EQUALS("cast < char * > ( p ) ;", tok("cast(p);")); ASSERT_EQUALS("return ( a + b ) * c ;", tok("return (a+b)*c;")); ASSERT_EQUALS("void f ( ) { int p ; if ( 2 * p == 0 ) { } }", tok("void f(){int p; if (2*p == 0) {}}")); ASSERT_EQUALS("void f ( ) { DIR * f ; f = opendir ( dirname ) ; if ( closedir ( f ) ) { } }", tok("void f(){DIR * f = opendir(dirname);if (closedir(f)){}}")); ASSERT_EQUALS("void foo ( int p ) { if ( p >= 0 ) { ; } }", tok("void foo(int p){if((p)>=0);}")); } void declareVar() { const char code[] = "void f ( ) { char str [ 100 ] = \"100\" ; }"; ASSERT_EQUALS(code, tok(code)); } void declareArray() { const char code1[] = "void f ( ) { char str [ ] = \"100\" ; }"; const char expected1[] = "void f ( ) { char str [ 4 ] = \"100\" ; }"; ASSERT_EQUALS(expected1, tok(code1)); const char code2[] = "char str [ ] = \"\\x00\";"; const char expected2[] = "char str [ 2 ] = \"\\0\" ;"; ASSERT_EQUALS(expected2, tok(code2)); const char code3[] = "char str [ ] = \"\\0\";"; const char expected3[] = "char str [ 2 ] = \"\\0\" ;"; ASSERT_EQUALS(expected3, tok(code3)); const char code4[] = "char str [ ] = \"\\n\\n\";"; const char expected4[] = "char str [ 3 ] = \"\\n\\n\" ;"; ASSERT_EQUALS(expected4, tok(code4)); } void dontRemoveIncrement() { { const char code[] = "void f(int a)\n" "{\n" " if (a > 10)\n" " a = 5;\n" " else\n" " a = 10;\n" " a++;\n" "}\n"; ASSERT_EQUALS("void f ( int a ) { if ( a > 10 ) { a = 5 ; } else { a = 10 ; } a ++ ; }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" " if (a > 10)\n" " a = 5;\n" " else\n" " a = 10;\n" " ++a;\n" "}\n"; ASSERT_EQUALS("void f ( int a ) { if ( a > 10 ) { a = 5 ; } else { a = 10 ; } ++ a ; }", tok(code)); } } void removePostIncrement() { const char code[] = "void f(int &c)\n" "{\n" " c = 0;\n" " c++;\n" " if (c>0) { c++; }\n" " c++;\n" "}\n"; TODO_ASSERT_EQUALS("void f ( int & c ) { c = 3 ; { ; } ; }", "void f ( int & c ) { c = 1 ; { c ++ ; } c ++ ; }", tok(code)); } void removePreIncrement() { { const char code[] = "void f(int &c)\n" "{\n" " c = 0;\n" " ++c;\n" " if (c>0) { ++c; }\n" " ++c;\n" "}\n"; TODO_ASSERT_EQUALS("void f ( int & c ) { c = 3 ; { ; } ; }", "void f ( int & c ) { c = 1 ; { ++ c ; } ++ c ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" " char a[] = \"p\";\n" " ++a[0];\n" "}\n"; ASSERT_EQUALS("void f ( ) { char a [ 2 ] = \"p\" ; ++ a [ 0 ] ; }", tok(code)); } } void elseif1() { const char code[] = "else if(ab) { cd } else { ef }gh;"; ASSERT_EQUALS("\n\n##file 0\n1: else { if ( ab ) { cd } else { ef } } gh ;\n", tokenizeDebugListing(code)); // syntax error: assert there is no segmentation fault ASSERT_EQUALS("\n\n##file 0\n1: else if ( x ) { }\n", tokenizeDebugListing("else if (x) { }")); { const char src[] = "void f(int g,int f) {\n" "if(g==1) {poo();}\n" "else if( g == 2 )\n" "{\n" " if( f == 0 ){coo();}\n" " else if( f==1)\n" " goo();\n" "}\n" "}"; const char expected[] = "void f ( int g , int f ) " "{ " "if ( g == 1 ) { poo ( ) ; } " "else { " "if ( g == 2 ) " "{ " "if ( f == 0 ) { coo ( ) ; } " "else { " "if ( f == 1 ) " "{ " "goo ( ) ; " "} " "} " "} " "} " "}"; ASSERT_EQUALS(tok(expected), tok(src)); } // Ticket #6860 - lambdas { const char src[] = "( []{if (ab) {cd}else if(ef) { gh } else { ij }kl}() );"; const char expected[] = "\n\n##file 0\n1: ( [ ] { if ( ab ) { cd } else { if ( ef ) { gh } else { ij } } kl } ( ) ) ;\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(src)); } { const char src[] = "[ []{if (ab) {cd}else if(ef) { gh } else { ij }kl}() ];"; const char expected[] = "\n\n##file 0\n1: [ [ ] { if ( ab ) { cd } else { if ( ef ) { gh } else { ij } } kl } ( ) ] ;\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(src)); } { const char src[] = "= { []{if (ab) {cd}else if(ef) { gh } else { ij }kl}() }"; const char expected[] = "\n\n##file 0\n1: = { [ ] { if ( ab ) { cd } else { if ( ef ) { gh } else { ij } } kl } ( ) }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(src)); } } unsigned int sizeofFromTokenizer(const char type[]) { Tokenizer tokenizer(&settings0, this); tokenizer.fillTypeSizes(); Token tok1(0); tok1.str(type); return tokenizer.sizeOfType(&tok1); } void sizeof_array() { const char *code; code = "void foo()\n" "{\n" " int i[4];\n" " sizeof(i);\n" " sizeof(*i);\n" "}\n"; ASSERT_EQUALS("void foo ( ) { int i [ 4 ] ; 16 ; 4 ; }", tok(code)); code = "static int i[4];\n" "void f()\n" "{\n" " int i[10];\n" " sizeof(i);\n" "}\n"; ASSERT_EQUALS("static int i [ 4 ] ; void f ( ) { int i [ 10 ] ; 40 ; }", tok(code)); { code = "int i[10];\n" "sizeof(i[0]);\n"; ASSERT_EQUALS("int i [ 10 ] ; 4 ;", tok(code)); code = "int i[10];\n" "sizeof i[0];\n"; ASSERT_EQUALS("int i [ 10 ] ; 4 ;", tok(code)); } code = "char i[2][20];\n" "sizeof(i[1]);\n" "sizeof(i);"; ASSERT_EQUALS("char i [ 2 ] [ 20 ] ; 20 ; 40 ;", tok(code)); code = "char i[2][20][30];\n" "sizeof(i[1][4][2]);\n" "sizeof(***i);\n" "sizeof(i[1][4]);\n" "sizeof(**i);\n" "sizeof(i[1]);\n" "sizeof(*i);\n" "sizeof(i);"; ASSERT_EQUALS("char i [ 2 ] [ 20 ] [ 30 ] ; 1 ; 1 ; 30 ; 30 ; 600 ; 600 ; 1200 ;", tok(code)); code = "sizeof(char[20]);\n" "sizeof(char[20][3]);\n" "sizeof(char[unknown][3]);"; ASSERT_EQUALS("20 ; 60 ; sizeof ( char [ unknown ] [ 3 ] ) ;", tok(code)); } void sizeof5() { const char code[] = "const char * names[2];" "for (int i = 0; i != sizeof(names[0]); i++)" "{}"; std::ostringstream expected; expected << "const char * names [ 2 ] ; for ( int i = 0 ; i != " << sizeofFromTokenizer("*") << " ; i ++ ) { }"; ASSERT_EQUALS(expected.str(), tok(code)); } void sizeof6() { const char code[] = ";int i;\n" "sizeof(i);\n"; std::ostringstream expected; expected << "; int i ; " << sizeof(int) << " ;"; ASSERT_EQUALS(expected.str(), tok(code)); } void sizeof7() { const char code[] = ";INT32 i[10];\n" "sizeof(i[0]);\n"; ASSERT_EQUALS("; INT32 i [ 10 ] ; sizeof ( i [ 0 ] ) ;", tok(code, true, Settings::Native)); ASSERT_EQUALS("; int i [ 10 ] ; 4 ;", tokWithWindows(code, true, Settings::Win32A)); } void sizeof8() { { const char code[] = "void f()\n" "{\n" " char* ptrs[2];\n" " a = sizeof( ptrs );\n" "}\n"; std::ostringstream oss; oss << (sizeofFromTokenizer("*") * 2); ASSERT_EQUALS("void f ( ) { char * ptrs [ 2 ] ; a = " + oss.str() + " ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" " char* ptrs[55];\n" " a = sizeof( ptrs );\n" "}\n"; std::ostringstream oss; oss << (sizeofFromTokenizer("*") * 55); ASSERT_EQUALS("void f ( ) { char * ptrs [ 55 ] ; a = " + oss.str() + " ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" " char* ptrs;\n" " a = sizeof( ptrs );\n" "}\n"; std::ostringstream oss; oss << sizeofFromTokenizer("*"); ASSERT_EQUALS("void f ( ) { a = " + oss.str() + " ; }", tok(code)); } } void sizeof9() { // ticket #487 { const char code[] = "; const char *str = \"1\"; sizeof(str);"; std::ostringstream expected; expected << "; const char * str ; str = \"1\" ; " << sizeofFromTokenizer("*") << " ;"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "; const char str[] = \"1\"; sizeof(str);"; std::ostringstream expected; expected << "; const char str [ 2 ] = \"1\" ; " << sizeofFromTokenizer("char")*2 << " ;"; ASSERT_EQUALS(expected.str(), tok(code)); } { // Ticket #799 const char code[] = "; const char str[] = {'1'}; sizeof(str);"; ASSERT_EQUALS("; const char str [ 1 ] = { '1' } ; 1 ;", tok(code)); } { // Ticket #2087 const char code[] = "; const char str[] = {\"abc\"}; sizeof(str);"; ASSERT_EQUALS("; const char str [ 4 ] = \"abc\" ; 4 ;", tok(code)); } // ticket #716 - sizeof string { std::ostringstream expected; expected << "; " << (sizeof "123") << " ;"; ASSERT_EQUALS(expected.str(), tok("; sizeof \"123\";")); ASSERT_EQUALS(expected.str(), tok("; sizeof(\"123\");")); } { const char code[] = "void f(char *a,char *b, char *c)" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( char * a , char * b , char * c ) { g ( " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "void f(char a,char b, char c)" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( char a , char b , char c ) { g ( " << sizeofFromTokenizer("char") << " , " << sizeofFromTokenizer("char") << " , " << sizeofFromTokenizer("char") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "void f(const char *a,const char *b, const char *c)" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( const char * a , const char * b , const char * c ) { g ( " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "void f(char a[10],char b[10], char c[10])" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( char a [ 10 ] , char b [ 10 ] , char c [ 10 ] ) { g ( " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "void f(const char a[10],const char b[10], const char c[10])" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( const char a [ 10 ] , " "const char b [ 10 ] , " "const char c [ 10 ] ) { g ( " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "void f(const char *a[10],const char *b[10], const char *c[10])" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( const char * a [ 10 ] , " "const char * b [ 10 ] , " "const char * c [ 10 ] ) { g ( " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { const char code[] = "void f(char *a[10],char *b[10], char *c[10])" "{g(sizeof(a),sizeof(b),sizeof(c));}"; std::ostringstream expected; expected << "void f ( char * a [ 10 ] , char * b [ 10 ] , char * c [ 10 ] ) { g ( " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " , " << sizeofFromTokenizer("*") << " ) ; }"; ASSERT_EQUALS(expected.str(), tok(code)); } { std::ostringstream expected; expected << "; " << sizeof("\"quote\""); ASSERT_EQUALS(expected.str(), tok("; sizeof(\"\\\"quote\\\"\")")); } { std::ostringstream expected; expected << "void f ( ) { char str [ 100 ] = \"100\" ; " << sizeofFromTokenizer("char")*100 << " }"; ASSERT_EQUALS(expected.str(), tok("void f ( ) { char str [ 100 ] = \"100\" ; sizeof ( str ) }")); } } void sizeof10() { // ticket #809 const char code[] = "int m ; " "compat_ulong_t um ; " "long size ; size = sizeof ( m ) / sizeof ( um ) ;"; ASSERT_EQUALS(code, tok(code, true, Settings::Win32A)); } void sizeof11() { // ticket #827 const char code[] = "void f()\n" "{\n" " char buf2[4];\n" " sizeof buf2;\n" "}\n" "\n" "void g()\n" "{\n" " struct A a[2];\n" " char buf[32];\n" " sizeof buf;\n" "}"; const char expected[] = "void f ( ) " "{" " char buf2 [ 4 ] ;" " 4 ; " "} " "" "void g ( ) " "{" " struct A a [ 2 ] ;" " char buf [ 32 ] ;" " 32 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void sizeof12() { // ticket #827 const char code[] = "void f()\n" "{\n" " int *p;\n" " (sizeof *p);\n" "}"; const char expected[] = "void f ( ) " "{" "" " 4 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void sizeof13() { // ticket #851 const char code[] = "int main()\n" "{\n" " char *a;\n" " a = malloc(sizeof(*a));\n" "}\n" "\n" "struct B\n" "{\n" " char * b[2];\n" "};"; const char expected[] = "int main ( ) " "{" " char * a ;" " a = malloc ( 1 ) ; " "} " "struct B " "{" " char * b [ 2 ] ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void sizeof14() { // ticket #954 const char code[] = "void f()\n" "{\n" " A **a;\n" " int aa = sizeof *(*a)->b;\n" "}\n"; const char expected[] = "void f ( ) " "{" " A * * a ;" " int aa ; aa = sizeof ( * ( * a ) . b ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); // #5064 - sizeof !! (a == 1); ASSERT_EQUALS("sizeof ( ! ! ( a == 1 ) ) ;", tok("sizeof !!(a==1);")); } void sizeof15() { // ticket #1020 tok("void f()\n" "{\n" " int *n;\n" " sizeof *(n);\n" "}"); ASSERT_EQUALS("", errout.str()); } void sizeof16() { // ticket #1027 const char code[] = "void f()\n" "{\n" " int a;\n" " printf(\"%i\", sizeof a++);\n" "}\n"; ASSERT_EQUALS("void f ( ) { int a ; printf ( \"%i\" , sizeof ( a ++ ) ) ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } void sizeof17() { // ticket #1050 const char code[] = "void f()\n" "{\n" " sizeof 1;\n" " while (0);\n" "}\n"; ASSERT_EQUALS("void f ( ) { sizeof ( 1 ) ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } void sizeof18() { { std::ostringstream expected; expected << sizeof(short int); { const char code[] = "void f()\n" "{\n" " sizeof(short int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(unsigned short int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(short unsigned int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(signed short int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } } { std::ostringstream expected; expected << sizeof(long long); { const char code[] = "void f()\n" "{\n" " sizeof(long long);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(signed long long);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(unsigned long long);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(long unsigned long);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(long long int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(signed long long int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(unsigned long long int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(long unsigned long int);\n" "}\n"; ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } } { const char code[] = "void f()\n" "{\n" " sizeof(char*);\n" "}\n"; std::ostringstream expected; expected << sizeof(int*); ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof(unsigned int*);\n" "}\n"; std::ostringstream expected; expected << sizeof(int*); ASSERT_EQUALS("void f ( ) { " + expected.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } } void sizeof19() { // ticket #1891 - sizeof 'x' { const char code[] = "void f()\n" "{\n" " sizeof 'x';\n" "}\n"; std::ostringstream sz; sz << sizeof('x'); ASSERT_EQUALS("void f ( ) { " + sz.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f()\n" "{\n" " sizeof('x');\n" "}\n"; std::ostringstream sz; sz << sizeof('x'); ASSERT_EQUALS("void f ( ) { " + sz.str() + " ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } } void sizeof20() { // ticket #2024 - sizeof a) const char code[] = "struct struct_a {\n" " char a[20];\n" "};\n" "\n" "void foo() {\n" " struct_a a;\n" " append(sizeof a).append();\n" "}\n"; ASSERT_EQUALS("struct struct_a { char a [ 20 ] ; } ; " "void foo ( ) {" " struct_a a ;" " append ( sizeof ( a ) ) . append ( ) ; " "}", tok(code)); } void sizeof21() { // ticket #2232 - sizeof...(Args) const char code[] = "struct Internal {\n" " int operator()(const Args&... args) const {\n" " int n = sizeof...(Args);\n" " return n;\n" " }\n" "};\n" "\n" "int main() {\n" " Internal internal;\n" " int n = 0; n = internal(1);\n" " return 0;\n" "}\n"; // don't segfault tok(code); } void sizeof22() { // sizeof from library const char code[] = "foo(sizeof(uint32_t), sizeof(std::uint32_t));"; TODO_ASSERT_EQUALS("foo ( 4 , 4 ) ;", "foo ( 4 , sizeof ( std :: uint32_t ) ) ;", tokWithStdLib(code)); } void sizeofsizeof() { // ticket #1682 const char code[] = "void f()\n" "{\n" " sizeof sizeof 1;\n" "}\n"; ASSERT_EQUALS("void f ( ) { sizeof ( sizeof ( 1 ) ) ; }", tok(code)); ASSERT_EQUALS("", errout.str()); } void casting() { { const char code[] = "void f()\n" "{\n" "for (int i = 0; i < static_cast(3); ++i) {}\n" "}\n"; const char expected[] = "void f ( ) { for ( int i = 0 ; i < 3 ; ++ i ) { } }"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f()\n" "{\n" " p = const_cast qtu ();\n" "}\n"; const char expected[] = "void f ( ) { p = const_cast < char * > qtu ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); } { // ticket #645 const char code[] = "void f()\n" "{\n" " return dynamic_cast((bar()));\n" "}\n"; const char expected[] = "void f ( ) { return bar ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); } } void strlen1() { ASSERT_EQUALS("4", tok("strlen(\"abcd\")")); { const char code[] = "void f()\n" "{\n" " const char *s = \"abcd\";\n" " strlen(s);\n" "}\n"; const char expected[] = "void f ( ) " "{" " const char * s ;" " s = \"abcd\" ;" " 4 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f()\n" "{\n" " const char s [ ] = \"abcd\";\n" " strlen(s);\n" "}\n"; const char expected[] = "void f ( ) " "{" " const char s [ 5 ] = \"abcd\" ;" " 4 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } } void strlen2() { // #4530 - make sure calculation with strlen is simplified ASSERT_EQUALS("i = -4 ;", tok("i = (strlen(\"abcd\") - 8);")); } void namespaces() { { const char code[] = "namespace std { }"; ASSERT_EQUALS("", tok(code)); } { const char code[] = "; namespace std { }"; ASSERT_EQUALS(";", tok(code)); } { const char code[] = "using namespace std; namespace a{ namespace b{ void f(){} } }"; const char expected[] = "namespace a { namespace b { void f ( ) { } } }"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "namespace b{ void f(){} }"; const char expected[] = "namespace b { void f ( ) { } }"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f(int namespace) { }"; const char expected[] = "void f ( int namespace ) { }"; ASSERT_EQUALS(expected, tok(code)); } } std::string simplifyIfAndWhileAssign(const char code[]) { // tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyIfAndWhileAssign(); return tokenizer.tokens()->stringifyList(0, false); } void ifassign1() { ASSERT_EQUALS("; a = b ; if ( a ) { ; }", simplifyIfAndWhileAssign(";if(a=b);")); ASSERT_EQUALS("; a = b ( ) ; if ( a ) { ; }", simplifyIfAndWhileAssign(";if((a=b()));")); ASSERT_EQUALS("; a = b ( ) ; if ( ! ( a ) ) { ; }", simplifyIfAndWhileAssign(";if(!(a=b()));")); ASSERT_EQUALS("; a . x = b ( ) ; if ( ! ( a . x ) ) { ; }", simplifyIfAndWhileAssign(";if(!(a->x=b()));")); ASSERT_EQUALS("void f ( ) { A ( ) a = b ; if ( a ) { ; } }", simplifyIfAndWhileAssign("void f() { A() if(a=b); }")); ASSERT_EQUALS("void foo ( int a ) { a = b ( ) ; if ( a >= 0 ) { ; } }", tok("void foo(int a) {if((a=b())>=0);}")); TODO_ASSERT_EQUALS("void foo ( A a ) { a . c = b ( ) ; if ( 0 <= a . c ) { ; } }", "void foo ( A a ) { a . c = b ( ) ; if ( a . c >= 0 ) { ; } }", tok("void foo(A a) {if((a.c=b())>=0);}")); } void ifAssignWithCast() { const char *code = "void foo()\n" "{\n" "FILE *f;\n" "if( (f = fopen(\"foo\", \"r\")) == ((FILE*)NULL) )\n" "return(-1);\n" "fclose(f);\n" "}\n"; const char *expected = "void foo ( ) " "{ " "FILE * f ; " "f = fopen ( \"foo\" , \"r\" ) ; " "if ( f == NULL ) " "{ " "return -1 ; " "} " "fclose ( f ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void whileAssign1() { ASSERT_EQUALS("; a = b ; while ( a ) { b = 0 ; a = b ; }", simplifyIfAndWhileAssign(";while(a=b) { b = 0; }")); ASSERT_EQUALS("; a . b = c ; while ( a . b ) { c = 0 ; a . b = c ; }", simplifyIfAndWhileAssign(";while(a.b=c) { c=0; }")); ASSERT_EQUALS("struct hfs_bnode * node ; " "struct hfs_btree * tree ; " "node = tree . node_hash [ i ++ ] ; " "while ( node ) { node = tree . node_hash [ i ++ ] ; }", tok("struct hfs_bnode *node;" "struct hfs_btree *tree;" "while ((node = tree->node_hash[i++])) { }")); ASSERT_EQUALS("char * s ; s = new char [ 10 ] ; while ( ! s ) { s = new char [ 10 ] ; }", tok("char *s; while (0 == (s=new char[10])) { }")); } void whileAssign2() { // #1909 - Internal error tok("void f()\n" "{\n" " int b;\n" " while (b = sizeof (struct foo { int i0;}))\n" " ;\n" " if (!(0 <= b ))\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void whileAssign3() { // #4254 - Variable id const char code[] = "void f() {\n" " int a;\n" " while (a = x());\n" "}"; ASSERT_EQUALS("\n\n##file 0\n" "1: void f ( ) {\n" "2: int a@1 ;\n" "3: a@1 = x ( ) ; while ( a@1 ) { ; a@1 = x ( ) ; }\n" "4: }\n", tokenizeDebugListing(code, true, "test.c")); } void whileAssign4() { errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr("; while (!(m = q->push(x))) {}"); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("; m = q . push < Message > ( x ) ; while ( ! m ) { m = q . push < Message > ( x ) ; }", tokenizer.tokens()->stringifyList(0, false)); ASSERT(tokenizer.tokens()->tokAt(26) != nullptr); if (tokenizer.tokens()->tokAt(26)) { ASSERT(tokenizer.tokens()->linkAt(6) == tokenizer.tokens()->tokAt(8)); ASSERT(tokenizer.tokens()->linkAt(24) == tokenizer.tokens()->tokAt(26)); } } void doWhileAssign() { ASSERT_EQUALS("; do { a = b ; } while ( a ) ;", simplifyIfAndWhileAssign(";do { } while(a=b);")); ASSERT_EQUALS("; do { a . a = 0 ; a . b = c ; } while ( a . b ) ;", simplifyIfAndWhileAssign(";do { a.a = 0; } while(a.b=c);")); ASSERT_EQUALS("struct hfs_bnode * node ; " "struct hfs_btree * tree ; " "do { node = tree . node_hash [ i ++ ] ; } while ( node ) ;", tok("struct hfs_bnode *node;" "struct hfs_btree *tree;" "do { } while((node = tree->node_hash[i++]));")); ASSERT_EQUALS("char * s ; do { s = new char [ 10 ] ; } while ( ! s ) ;", tok("char *s; do { } while (0 == (s=new char[10]));")); // #4911 ASSERT_EQUALS("; do { current = f ( ) ; } while ( ( current ) != NULL ) ;", simplifyIfAndWhileAssign(";do { } while((current=f()) != NULL);")); } void not1() { ASSERT_EQUALS("void f ( ) { if ( ! p ) { ; } }", tok("void f() { if (not p); }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( p && ! q ) { ; } }", tok("void f() { if (p && not q); }", "test.c", false)); ASSERT_EQUALS("void f ( ) { a = ! ( p && q ) ; }", tok("void f() { a = not(p && q); }", "test.c", false)); // Don't simplify 'not' or 'compl' if they are defined as a type; // in variable declaration and in function declaration/definition ASSERT_EQUALS("struct not { int x ; } ;", tok("struct not { int x; };", "test.c", false)); ASSERT_EQUALS("void f ( ) { not p ; compl c ; }", tok(" void f() { not p; compl c; }", "test.c", false)); ASSERT_EQUALS("void foo ( not i ) ;", tok("void foo(not i);", "test.c", false)); ASSERT_EQUALS("int foo ( not i ) { return g ( i ) ; }", tok("int foo(not i) { return g(i); }", "test.c", false)); } void and1() { ASSERT_EQUALS("void f ( ) { if ( p && q ) { ; } }", tok("void f() { if (p and q) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) && q ) { ; } }", tok("void f() { if (foo() and q) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) && bar ( ) ) { ; } }", tok("void f() { if (foo() and bar()) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( p && bar ( ) ) { ; } }", tok("void f() { if (p and bar()) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( p && ! q ) { ; } }", tok("void f() { if (p and not q) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { r = a && b ; }", tok("void f() { r = a and b; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { r = ( a || b ) && ( c || d ) ; }", tok("void f() { r = (a || b) and (c || d); }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( test1 [ i ] == 'A' && test2 [ i ] == 'C' ) { } }", tok("void f() { if (test1[i] == 'A' and test2[i] == 'C') {} }", "test.c", false)); } void or1() { ASSERT_EQUALS("void f ( ) { if ( p || q ) { ; } }", tok("void f() { if (p or q) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) || q ) { ; } }", tok("void f() { if (foo() or q) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) || bar ( ) ) { ; } }", tok("void f() { if (foo() or bar()) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( p || bar ( ) ) { ; } }", tok("void f() { if (p or bar()) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { if ( p || ! q ) { ; } }", tok("void f() { if (p or not q) ; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { r = a || b ; }", tok("void f() { r = a or b; }", "test.c", false)); ASSERT_EQUALS("void f ( ) { r = ( a && b ) || ( c && d ) ; }", tok("void f() { r = (a && b) or (c && d); }", "test.c", false)); } void cAlternativeTokens() { ASSERT_EQUALS("void f ( ) { err |= ( ( r & s ) && ! t ) ; }", tok("void f() { err or_eq ((r bitand s) and not t); }", "test.c", false)); ASSERT_EQUALS("void f ( ) const { r = f ( a [ 4 ] | 15 , ~ c , ! d ) ; }", tok("void f() const { r = f(a[4] bitor 0x0F, compl c, not d) ; }", "test.c", false)); } void comma_keyword() { { const char code[] = "void foo()\n" "{\n" " char *a, *b;\n" " delete a, delete b;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { char * a ; char * b ; delete a ; delete b ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " struct A *a, *b;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { struct A * a ; struct A * b ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " struct A **a, **b;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { struct A * * a ; struct A * * b ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " char *a, *b;\n" " delete a, b;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { char * a ; char * b ; delete a ; b ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " char *a, *b, *c;\n" " delete a, b, c;\n" "}\n"; // delete a; b; c; would be better but this will do too ASSERT_EQUALS("void foo ( ) { char * a ; char * b ; char * c ; delete a ; b , c ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " char *a, *b;\n" " if (x) \n" " delete a, b;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { char * a ; char * b ; if ( x ) { delete a ; b ; } }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " char *a, *b, *c;\n" " if (x) \n" " delete a, b, c;\n" "}\n"; // delete a; b; c; would be better but this will do too ASSERT_EQUALS("void foo ( ) { char * a ; char * b ; char * c ; if ( x ) { delete a ; b , c ; } }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " char **a, **b, **c;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { char * * a ; char * * b ; char * * c ; }", tok(code)); } { const char code[] = "int f()\n" "{\n" " if (something)\n" " return a(2, c(3, 4)), b(3), 10;\n" " return a(), b(0, 0, 0), 10;\n" "}\n"; ASSERT_EQUALS("int f ( )" " {" " if ( something )" " {" " a ( 2 , c ( 3 , 4 ) ) ;" " b ( 3 ) ;" " return 10 ;" " }" " a ( ) ;" " b ( 0 , 0 , 0 ) ;" " return 10 ; " "}", tok(code)); } { const char code[] = "void foo()\n" "{\n" " delete [] a, a = 0;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { delete [ ] a ; a = 0 ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " delete a, a = 0;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { delete a ; a = 0 ; }", tok(code)); } { const char code[] = "void foo()\n" "{\n" " if( x ) delete a, a = 0;\n" "}\n"; ASSERT_EQUALS("void foo ( ) { if ( x ) { delete a ; a = 0 ; } }", tok(code)); } { const char code[] = "void f()\n" "{\n" " for(int a,b; a < 10; a = a + 1, b = b + 1);\n" "}\n"; ASSERT_EQUALS("void f ( ) { for ( int a , b ; a < 10 ; a = a + 1 , b = b + 1 ) { ; } }", tok(code)); } { const char code[] = "void f()\n" "{\n" " char buf[BUFSIZ], **p;\n" " char *ptrs[BUFSIZ], **pp;\n" "}\n"; ASSERT_EQUALS("void f ( ) { char buf [ BUFSIZ ] ; char * * p ; char * ptrs [ BUFSIZ ] ; char * * pp ; }", tok(code)); } { // #4786 - don't replace , with ; in ".. : public B, C .." code const char code[] = "template < class T = X > class A : public B , C { } ;"; ASSERT_EQUALS(code, tok(code)); } } void remove_comma() { { const char code[] = "void f()\n" "{\n" " int a,b;\n" " if( a )\n" " a=0,\n" " b=0;\n" "}\n"; ASSERT_EQUALS("void f ( ) { int a ; int b ; if ( a ) { a = 0 ; b = 0 ; } }", tok(code)); } { ASSERT_EQUALS("a ? ( b = c , d ) : e ;", tok("a ? b = c , d : e ;")); // Keep comma } { ASSERT_EQUALS("; return a ? ( b = c , d ) : e ;", tok("; return a ? b = c , d : e ;")); // Keep comma } { const char code[] = "void f()\n" "{\n" " A a,b;\n" " if( a.f )\n" " a.f=b.f,\n" " a.g=b.g;\n" "}\n"; ASSERT_EQUALS("void f ( ) { A a ; A b ; if ( a . f ) { a . f = b . f ; a . g = b . g ; } }", tok(code)); } // keep the comma in template specifiers.. { const char code[] = "void f()\n" "{\n" " int a = b, int>();\n" "}\n"; ASSERT_EQUALS("void f ( ) { int a ; a = b < T < char , 3 > , int > ( ) ; }", tok(code)); } { const char code[] = "void f() {\n" " a = new std::map;\n" "}\n"; ASSERT_EQUALS("void f ( ) { a = new std :: map < std :: string , std :: string > ; }", tok(code)); } { // ticket #1327 const char code[] = "const C<1,2,3> foo ()\n" "{\n" " return C<1,2,3>(x,y);\n" "}\n"; const char expected[] = "const C < 1 , 2 , 3 > foo ( ) " "{" " return C < 1 , 2 , 3 > ( x , y ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "int foo ()\n" "{\n" " return doSomething(), 0;\n" "}\n"; const char expected[] = "int foo ( ) " "{" " doSomething ( ) ; return 0 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "int foo ()\n" "{\n" " return a=1, b=2;\n" "}\n"; const char expected[] = "int foo ( ) " "{" " a = 1 ; return b = 2 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "tr = (struct reg){ .a = (1), .c = (2) };"; const char expected[] = "tr = ( struct reg ) { . a = 1 , . c = 2 } ;"; ASSERT_EQUALS(expected, tok(code)); } } void simplifyConditionOperator() { { const char code[] = "(0?(false?1:2):3);"; ASSERT_EQUALS("( 3 ) ;", tok(code)); } { const char code[] = "(1?(false?1:2):3);"; ASSERT_EQUALS("( 2 ) ;", tok(code)); } { const char code[] = "int a = (1?0:1 == 1?0:1);"; ASSERT_EQUALS("int a ; a = 0 ;", tok(code)); } { const char code[] = "(1?0:foo());"; ASSERT_EQUALS("( 0 ) ;", tok(code)); } { const char code[] = "void f () { switch(n) { case 1?0:foo(): break; }}"; ASSERT_EQUALS("void f ( ) { switch ( n ) { case 0 : ; break ; } }", tok(code)); } { const char code[] = "void f () { switch(n) { case 1?0?1:0:foo(): break; }}"; TODO_ASSERT_EQUALS("void f ( ) { switch ( n ) { case 0 : ; break ; } }", "void f ( ) { switch ( n ) { case ( 0 ) : ; break ; } }", tok(code)); } { const char code[] = "void f () { switch(n) { case 0?foo():1: break; }}"; ASSERT_EQUALS("void f ( ) { switch ( n ) { case 1 : ; break ; } }", tok(code)); } { const char code[] = "( true ? a ( ) : b ( ) );"; ASSERT_EQUALS("( a ( ) ) ;", tok(code)); } { const char code[] = "( true ? abc . a : abc . b );"; ASSERT_EQUALS("( abc . a ) ;", tok(code)); } { const char code[] = "void f()\n" "{\n" " bool x = false;\n" " int b = x ? 44 : 3;\n" "}\n"; ASSERT_EQUALS("void f ( ) { }", tok(code)); } { const char code[] = "int vals[] = { 0x13, 1?0x01:0x00 };"; ASSERT_EQUALS("int vals [ 2 ] = { 19 , 1 } ;", tok(code)); } { const char code[] = "int vals[] = { 0x13, 0?0x01:0x00 };"; ASSERT_EQUALS("int vals [ 2 ] = { 19 , 0 } ;", tok(code)); } { const char code[] = "a = 1 ? 0 : ({ 0; });"; ASSERT_EQUALS("a = 0 ;", tok(code)); } //GNU extension: "x ?: y" <-> "x ? x : y" { const char code[] = "; a = 1 ? : x; b = 0 ? : 2;"; ASSERT_EQUALS("; a = 1 ; b = 2 ;", tok(code)); } // Ticket #3572 (segmentation fault) ASSERT_EQUALS("0 ; x = { ? y : z ; }", tok("0; x = { ? y : z; }")); { // #3922 - (true) ASSERT_EQUALS("; x = 2 ;", tok("; x = (true)?2:4;")); ASSERT_EQUALS("; x = 4 ;", tok("; x = (false)?2:4;")); ASSERT_EQUALS("; x = * a ;", tok("; x = (true)?*a:*b;")); ASSERT_EQUALS("; x = * b ;", tok("; x = (false)?*a:*b;")); ASSERT_EQUALS("void f ( ) { return 1 ; }", tok("void f() { char *p=0; return (p==0)?1:2; }")); } } void calculations() { { const char code[] = "a[i+8+2];"; ASSERT_EQUALS("a [ i + 10 ] ;", tok(code)); } { const char code[] = "a[8+2+i];"; ASSERT_EQUALS("a [ 10 + i ] ;", tok(code)); } { const char code[] = "a[i + 2 * (2 * 4)];"; ASSERT_EQUALS("a [ i + 16 ] ;", tok(code)); } { const char code[] = "a[i + 100 - 90];"; ASSERT_EQUALS("a [ i + 10 ] ;", tok(code)); } { const char code[] = "a[1+1+1+1+1+1+1+1+1+1-2+5-3];"; ASSERT_EQUALS("a [ 10 ] ;", tok(code)); } { const char code[] = "a[10+10-10-10];"; ASSERT_EQUALS("a [ 0 ] ;", tok(code)); } ASSERT_EQUALS("a [ 4 ] ;", tok("a[1+3|4];")); ASSERT_EQUALS("x = 1 + 2 * y ;", tok("x=1+2*y;")); ASSERT_EQUALS("x = 7 ;", tok("x=1+2*3;")); ASSERT_EQUALS("x = 47185 ;", tok("x=(65536*72/100);")); ASSERT_EQUALS("x = 900 ;", tok("x = 1500000 / ((145000 - 55000) * 1000 / 54000);")); ASSERT_EQUALS("int a [ 8 ] ;", tok("int a[5+6/2];")); ASSERT_EQUALS("int a [ 4 ] ;", tok("int a[(10)-1-5];")); ASSERT_EQUALS("int a [ i - 9 ] ;", tok("int a[i - 10 + 1];")); ASSERT_EQUALS("x = y ;", tok("x=0+y+0-0;")); ASSERT_EQUALS("x = 0 ;", tok("x=0*y;")); ASSERT_EQUALS("x = 501 ;", tok("x = 1000 + 2 >> 1;")); ASSERT_EQUALS("x = 125 ;", tok("x = 1000 / 2 >> 2;")); { // Ticket #1997 const char code[] = "void * operator new[](size_t);"; ASSERT_EQUALS("void * operatornew[] ( long ) ;", tok(code, true, Settings::Win32A)); } ASSERT_EQUALS("; a [ 0 ] ;", tok(";a[0*(*p)];")); ASSERT_EQUALS(";", tok("; x = x + 0;")); ASSERT_EQUALS("if ( a == 2 ) { ; }", tok("if (a==1+1);")); ASSERT_EQUALS("if ( a + 2 != 6 ) { ; }", tok("if (a+1+1!=1+2+3);")); ASSERT_EQUALS("if ( 4 < a ) { ; }", tok("if (14-2*5>1+0%2*1);")); // #4931 ASSERT_EQUALS("x ( 0 & 4 != 1 ) ;", tok("x(4%1<<1&4!=1);")); // #4931 (can be simplified further but it's not a problem) ASSERT_EQUALS("x ( true ) ;", tok("x(0&&4>0==2||4);")); // #4931 // don't remove these spaces.. ASSERT_EQUALS("new ( auto ) ( 4 ) ;", tok("new (auto)(4);")); } void comparisons() { ASSERT_EQUALS("( 1 ) ;", tok("( 1 < 2 );")); ASSERT_EQUALS("( x && true ) ;", tok("( x && 1 < 2 );")); ASSERT_EQUALS("( 5 ) ;", tok("( 1 < 2 && 3 < 4 ? 5 : 6 );")); ASSERT_EQUALS("( 6 ) ;", tok("( 1 > 2 && 3 > 4 ? 5 : 6 );")); } void simplifyFlowControl() { const char code1[] = "void f() {\n" " return;\n" " y();\n" "}"; ASSERT_EQUALS("void f ( ) { return ; }", tokWithStdLib(code1)); const char code2[] = "void f() {\n" " exit(0);\n" " y();\n" "}"; ASSERT_EQUALS("void f ( ) { exit ( 0 ) ; }", tokWithStdLib(code2)); const char code3[] = "void f() {\n" " x.abort();\n" " y();\n" "}"; ASSERT_EQUALS("void f ( ) { x . abort ( ) ; y ( ) ; }", tokWithStdLib(code3)); } void flowControl() { { ASSERT_EQUALS("void f ( ) { exit ( 0 ) ; }", tokWithStdLib("void f() { exit(0); foo(); }")); ASSERT_EQUALS("void f ( ) { exit ( 0 ) ; }", tokWithStdLib("void f() { exit(0); if (m) foo(); }")); ASSERT_EQUALS("void f ( int n ) { if ( n ) { exit ( 0 ) ; } foo ( ) ; }", tokWithStdLib("void f(int n) { if (n) { exit(0); } foo(); }")); ASSERT_EQUALS("void f ( ) { exit ( 0 ) ; }", tokWithStdLib("void f() { exit(0); dead(); switch (n) { case 1: deadcode () ; default: deadcode (); } }")); ASSERT_EQUALS("int f ( int n ) { switch ( n ) { case 0 : ; exit ( 0 ) ; default : ; exit ( 0 ) ; } exit ( 0 ) ; }", tokWithStdLib("int f(int n) { switch (n) {case 0: exit(0); n*=2; default: exit(0); n*=6;} exit(0); foo();}")); //ticket #3132 ASSERT_EQUALS("void f ( int i ) { goto label ; { label : ; exit ( 0 ) ; } }", tokWithStdLib("void f (int i) { goto label; switch(i) { label: exit(0); } }")); //ticket #3148 ASSERT_EQUALS("void f ( ) { MACRO ( exit ( 0 ) ) }", tokWithStdLib("void f() { MACRO(exit(0)) }")); ASSERT_EQUALS("void f ( ) { MACRO ( exit ( 0 ) ; , NULL ) }", tokWithStdLib("void f() { MACRO(exit(0);, NULL) }")); ASSERT_EQUALS("void f ( ) { MACRO ( bar1 , exit ( 0 ) ) }", tokWithStdLib("void f() { MACRO(bar1, exit(0)) }")); ASSERT_EQUALS("void f ( ) { MACRO ( exit ( 0 ) ; bar2 , foo ) }", tokWithStdLib("void f() { MACRO(exit(0); bar2, foo) }")); } { const char* code = "void f(){ " " if (k>0) goto label; " " exit(0); " " if (tnt) " " { " " { " " check(); " " k=0; " " } " " label: " " bar(); " " } " "}"; ASSERT_EQUALS("void f ( ) { if ( k > 0 ) { goto label ; } exit ( 0 ) ; { label : ; bar ( ) ; } }", tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " {" " boo();" " while (n) { --n; }" " {" " label:" " ok();" " }" " }" "}"; ASSERT_EQUALS("void foo ( ) { exit ( 0 ) ; { label : ; ok ( ) ; } }", tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (n) {" " case 1:" " label:" " foo(); break;" " default:" " break;" " }" "}"; const char* expected = "void foo ( ) { exit ( 0 ) ; { label : ; foo ( ) ; break ; } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (n) {" " case 1:" " {" " foo();" " }" " label:" " bar();" " }" "}"; const char* expected = "void foo ( ) { exit ( 0 ) ; { label : ; bar ( ) ; } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (n) {" " case a:" " {" " foo();" " }" " case b|c:" " bar();" " }" "}"; const char* expected = "void foo ( ) { exit ( 0 ) ; }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (n) {" " case 1:" " label:" " foo(); break;" " default:" " break; break;" " }" "}"; const char* expected = "void foo ( ) { exit ( 0 ) ; { label : ; foo ( ) ; break ; } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (n) {" " case 1:" " label:" " foo(); break; break;" " default:" " break;" " }" "}"; const char* expected = "void foo ( ) { exit ( 0 ) ; { label : ; foo ( ) ; break ; } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (n) {" " case 1:" " label:" " foo(); break; break;" " default:" " break; break;" " }" "}"; const char* expected = "void foo ( ) { exit ( 0 ) ; { label : ; foo ( ) ; break ; } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "int f() { " "switch (x) { case 1: exit(0); bar(); tack; { ticak(); exit(0) } exit(0);" "case 2: exit(0); { random(); } tack(); " "switch(y) { case 1: exit(0); case 2: exit(0); } " "exit(0); } exit(0); }"; ASSERT_EQUALS("int f ( ) { switch ( x ) { case 1 : ; exit ( 0 ) ; case 2 : ; exit ( 0 ) ; } exit ( 0 ) ; }",tokWithStdLib(code)); } { const char* code = "int f() {" "switch (x) { case 1: exit(0); bar(); tack; { ticak(); exit(0); } exit(0);" "case 2: switch(y) { case 1: exit(0); bar2(); foo(); case 2: exit(0); }" "exit(0); } exit(0); }"; const char* expected = "int f ( ) {" " switch ( x ) { case 1 : ; exit ( 0 ) ;" " case 2 : ; switch ( y ) { case 1 : ; exit ( 0 ) ; case 2 : ; exit ( 0 ) ; }" " exit ( 0 ) ; } exit ( 0 ) ; }"; ASSERT_EQUALS(expected,tokWithStdLib(code)); } { const char* code = "void foo () {" " switch (i) { case 0: switch (j) { case 0: exit(0); }" " case 1: switch (j) { case -1: exit(0); }" " case 2: switch (j) { case -2: exit(0); }" " case 3: if (blah6) {exit(0);} break; } }"; const char* expected = "void foo ( ) {" " switch ( i ) { case 0 : ; switch ( j ) { case 0 : ; exit ( 0 ) ; }" " case 1 : ; switch ( j ) { case -1 : ; exit ( 0 ) ; }" " case 2 : ; switch ( j ) { case -2 : ; exit ( 0 ) ; }" " case 3 : ; if ( blah6 ) { exit ( 0 ) ; } break ; } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo () {" " exit(0);" " switch (i) { case 0: switch (j) { case 0: foo(); }" " case 1: switch (j) { case -1: bar(); label:; ok(); }" " case 3: if (blah6) { boo(); break; } } }"; const char* expected = "void foo ( ) { exit ( 0 ) ; { { label : ; ok ( ) ; } case 3 : ; if ( blah6 ) { boo ( ) ; break ; } } }"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char* code = "void foo() {" " switch ( t ) {" " case 0:" " if ( t ) switch ( b ) {}" " break;" " case 1:" " exit(0);" " return 0;" " }" " return 0;" "}"; const char* expected = "void foo ( ) {" " switch ( t ) {" " case 0 : ;" " if ( t ) { switch ( b ) { } }" " break ;" " case 1 : ;" " exit ( 0 ) ;" " }" " return 0 ; " "}"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char code[] = "void foo()\n" "{\n" " A *a = 0;\n" " if (!a) {\n" " nondeadcode;\n" " return;\n" " dead;\n" " }\n" " stilldead;\n" " a->_a;\n" "}\n"; const char expected[] = "void foo ( ) " "{" " A * a ; a = 0 ; {" " nondeadcode ;" " return ;" " } " "}"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char code[] = "class Fred\n" "{\n" "public:\n" " bool foo() const { return f; }\n" " bool exit();\n" "\n" "private:\n" " bool f;\n" "};\n"; const char expected[] = "class Fred " "{" " public:" " bool foo ( ) const { return f ; }" " bool exit ( ) ;" "" " private:" " bool f ; " "} ;"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } { const char code[] = "class abort { };\n" "\n" "class Fred\n" "{\n" " public:\n" " bool foo() const { return f; }\n" " abort exit();\n" "\n" " private:\n" "bool f;\n" "};\n"; const char expected[] = "class abort { } ; " "class Fred " "{" " public:" " bool foo ( ) const { return f ; }" " abort exit ( ) ;" "" " private:" " bool f ; " "} ;"; ASSERT_EQUALS(expected, tokWithStdLib(code)); } ASSERT_EQUALS("void foo ( ) { exit ( 0 ) ; }", tokWithStdLib("void foo() { do { exit(0); } while (true); }")); // #6187 tokWithStdLib("void foo() {\n" " goto label;\n" " for (int i = 0; i < 0; ++i) {\n" " ;\n" "label:\n" " ;\n" " }\n" "}"); } void strcat1() { const char code[] = "; strcat(strcat(strcat(strcat(strcat(strcat(dst, \"this \"), \"\"), \"is \"), \"a \"), \"test\"), \".\");"; const char expect[] = "; " "strcat ( dst , \"this \" ) ; " "strcat ( dst , \"\" ) ; " "strcat ( dst , \"is \" ) ; " "strcat ( dst , \"a \" ) ; " "strcat ( dst , \"test\" ) ; " "strcat ( dst , \".\" ) ;"; ASSERT_EQUALS(expect, tok(code)); } void strcat2() { const char code[] = "; strcat(strcat(dst, foo[0]), \" \");"; const char expect[] = "; " "strcat ( dst , foo [ 0 ] ) ; " "strcat ( dst , \" \" ) ;"; ASSERT_EQUALS(expect, tok(code)); } void simplifyAtol() { ASSERT_EQUALS("a = std :: atol ( x ) ;", tok("a = std::atol(x);")); ASSERT_EQUALS("a = atol ( \"text\" ) ;", tok("a = atol(\"text\");")); ASSERT_EQUALS("a = 0 ;", tok("a = std::atol(\"0\");")); ASSERT_EQUALS("a = 10 ;", tok("a = atol(\"0xa\");")); } void simplifyOperator1() { // #3237 - error merging namespaces with operators const char code[] = "class c {\n" "public:\n" " operator std::string() const;\n" " operator string() const;\n" "};\n"; const char expected[] = "class c { " "public: " "operatorstd::string ( ) const ; " "operatorstring ( ) const ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyOperator2() { // #6576 ASSERT_EQUALS("template < class T > class SharedPtr { " "SharedPtr & operator= ( SharedPtr < Y > const & r ) ; " "} ; " "class TClass { " "public: TClass & operator= ( const TClass & rhs ) ; " "} ; " "TClass :: TClass ( const TClass & other ) { operator= ( other ) ; }", tok("template\n" " class SharedPtr {\n" " SharedPtr& operator=(SharedPtr const & r);\n" "};\n" "class TClass {\n" "public:\n" " TClass& operator=(const TClass& rhs);\n" "};\n" "TClass::TClass(const TClass &other) {\n" " operator=(other);\n" "}")); } void simplifyArrayAccessSyntax() { ASSERT_EQUALS("\n\n##file 0\n" "1: int a@1 ; a@1 [ 13 ] ;\n", tokenizeDebugListing("int a; 13[a];")); } void simplify_numeric_condition() { { const char code[] = "void f()\n" "{\n" "int x = 0;\n" "if( !x || 0 )\n" "{ g();\n" "}\n" "}"; ASSERT_EQUALS("void f ( ) { g ( ) ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" "int x = 1;\n" "if( !x )\n" "{ g();\n" "}\n" "}"; ASSERT_EQUALS("void f ( ) { }", tok(code)); } { const char code[] = "void f()\n" "{\n" "bool x = true;\n" "if( !x )\n" "{ g();\n" "}\n" "}"; ASSERT_EQUALS("void f ( ) { }", tok(code)); } { const char code[] = "void f()\n" "{\n" "bool x = false;\n" "if( !x )\n" "{ g();\n" "}\n" "}"; ASSERT_EQUALS("void f ( ) { g ( ) ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" " if (5==5);\n" "}\n"; ASSERT_EQUALS("void f ( ) { ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" " if (4<5);\n" "}\n"; ASSERT_EQUALS("void f ( ) { ; }", tok(code)); } { const char code[] = "void f()\n" "{\n" " if (5<5);\n" "}\n"; ASSERT_EQUALS("void f ( ) { }", tok(code)); } { const char code[] = "void f()\n" "{\n" " if (13>12?true:false);\n" "}\n"; ASSERT_EQUALS("void f ( ) { ; }", tok(code)); } { // #7849 const char code[] = "void f() {\n" "if (-1e-2 == -0.01) \n" " g();\n" "else\n" " h();\n" "}"; ASSERT_EQUALS("void f ( ) { if ( -1e-2 == -0.01 ) { g ( ) ; } else { h ( ) ; } }", tok(code)); } } void simplify_condition() { { const char code[] = "void f(int a)\n" "{\n" "if (a && false) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if (false && a) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if (true || a) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { g ( ) ; }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if (a || true) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { g ( ) ; }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if (a || true || b) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { g ( ) ; }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if (a && false && b) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if (a || (b && false && c) || d) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { if ( a || d ) { g ( ) ; } }", tok(code)); } { const char code[] = "void f(int a)\n" "{\n" "if ((a && b) || true || (c && d)) g();\n" "}"; ASSERT_EQUALS("void f ( int a ) { g ( ) ; }", tok(code)); } { // #4931 const char code[] = "void f() {\n" "if (12 && 7) g();\n" "}"; ASSERT_EQUALS("void f ( ) { g ( ) ; }", tok(code)); } } void pointeralias1() { { const char code[] = "void f(char *p1)\n" "{\n" " char *p = p1;\n" " p1 = 0;" " x(p);\n" "}\n"; const char expected[] = "void f ( char * p1 ) " "{ " "char * p ; p = p1 ; " "p1 = 0 ; " "x ( p ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void foo(Result* ptr)\n" "{\n" " Result* obj = ptr;\n" " ++obj->total;\n" "}\n"; const char expected[] = "void foo ( Result * ptr ) " "{ " "Result * obj ; obj = ptr ; " "++ obj . total ; " "}"; ASSERT_EQUALS(expected, tok(code)); } } void pointeralias2() { const char code[] = "void f()\n" "{\n" " int i;\n" " int *p = &i;\n" " return *p;\n" "}\n"; ASSERT_EQUALS("void f ( ) { int i ; return i ; }", tok(code)); } void pointeralias3() { const char code[] = "void f()\n" "{\n" " int i, j, *p;\n" " if (ab) p = &i;\n" " else p = &j;\n" " *p = 0;\n" "}\n"; const char expected[] = "void f ( ) " "{" " int i ; int j ; int * p ;" " if ( ab ) { p = & i ; }" " else { p = & j ; }" " * p = 0 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void pointeralias4() { const char code[] = "int f()\n" "{\n" " int i;\n" " int *p = &i;\n" " *p = 5;\n" " return i;\n" "}\n"; const char expected[] = "int f ( ) " "{" " return 5 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void while0() { ASSERT_EQUALS("; x = 1 ;", tok("; do { x = 1 ; } while (0);")); ASSERT_EQUALS("; return 0 ;", tok("; do { return 0; } while (0);")); ASSERT_EQUALS("void foo ( ) { goto label ; }", tok("void foo() { do { goto label; } while (0); }")); ASSERT_EQUALS("; { continue ; }", tok("; do { continue ; } while (0);")); ASSERT_EQUALS("; { break ; }", tok("; do { break; } while (0);")); ASSERT_EQUALS(";", tok("; while (false) { a; }")); ASSERT_EQUALS(";", tok("; while (false) { switch (n) { case 0: return; default: break; } n*=1; }")); } void while0for() { // for (condition is always false) ASSERT_EQUALS("void f ( ) { int i ; for ( i = 0 ; i < 0 ; i ++ ) { } }", tok("void f() { int i; for (i = 0; i < 0; i++) { a; } }")); //ticket #3140 ASSERT_EQUALS("void f ( ) { int i ; for ( i = 0 ; i < 0 ; i ++ ) { } }", tok("void f() { int i; for (i = 0; i < 0; i++) { foo(); break; } }")); ASSERT_EQUALS("void f ( ) { int i ; for ( i = 0 ; i < 0 ; i ++ ) { } }", tok("void f() { int i; for (i = 0; i < 0; i++) { foo(); continue; } }")); ASSERT_EQUALS("void f ( ) { }", tok("void f() { for (int i = 0; i < 0; i++) { a; } }")); ASSERT_EQUALS("void f ( ) { }", tok("void f() { for (unsigned int i = 0; i < 0; i++) { a; } }")); ASSERT_EQUALS("void f ( ) { }", tok("void f() { for (long long i = 0; i < 0; i++) { a; } }")); ASSERT_EQUALS("void f ( ) { }", tok("void f() { for (signed long long i = 0; i < 0; i++) { a; } }")); ASSERT_EQUALS("void f ( ) { }", tok("void f() { int n = 0; for (signed long long i = 0; i < n; i++) { a; } }")); // #8059 ASSERT_EQUALS("void f ( ) { int i ; for ( i = 0 ; i < 0 ; ++ i ) { } return i ; }", tok("void f() { int i; for (i=0;i<0;++i){ dostuff(); } return i; }")); } void while1() { // ticket #1197 const char code[] = "void do {} while (0) { }"; const char expected[] = "void { }"; ASSERT_EQUALS(expected, tok(code)); } void duplicateDefinition() { // #3565 - wrongly detects duplicate definition Tokenizer tokenizer(&settings0, this); std::istringstream istr("x ; return a not_eq x;"); tokenizer.tokenize(istr, "test.c"); Token *x_token = tokenizer.list.front()->tokAt(5); ASSERT_EQUALS(false, tokenizer.duplicateDefinition(&x_token)); } void removestd() { ASSERT_EQUALS("; strcpy ( a , b ) ;", tok("; std::strcpy(a,b);")); ASSERT_EQUALS("; strcat ( a , b ) ;", tok("; std::strcat(a,b);")); ASSERT_EQUALS("; strncpy ( a , b , 10 ) ;", tok("; std::strncpy(a,b,10);")); ASSERT_EQUALS("; strncat ( a , b , 10 ) ;", tok("; std::strncat(a,b,10);")); ASSERT_EQUALS("; free ( p ) ;", tok("; std::free(p);")); ASSERT_EQUALS("; malloc ( 10 ) ;", tok("; std::malloc(10);")); } void simplifyInitVar() { // ticket #1005 - int *p(0); => int *p = 0; { const char code[] = "void foo() { int *p(0); }"; ASSERT_EQUALS("void foo ( ) { }", tok(code)); } { const char code[] = "void foo() { int p(0); }"; ASSERT_EQUALS("void foo ( ) { }", tok(code)); } { const char code[] = "void a() { foo *p(0); }"; ASSERT_EQUALS("void a ( ) { }", tok(code)); } } void simplifyReference() { ASSERT_EQUALS("void f ( ) { int a ; a ++ ; }", tok("void f() { int a; int &b(a); b++; }")); ASSERT_EQUALS("void f ( ) { int a ; a ++ ; }", tok("void f() { int a; int &b = a; b++; }")); ASSERT_EQUALS("void test ( ) { c . f ( 7 ) ; }", tok("void test() { c.f(7); T3 &t3 = c; }")); // #6133 } void simplifyRealloc() { ASSERT_EQUALS("; free ( p ) ; p = 0 ;", tok("; p = realloc(p, 0);")); ASSERT_EQUALS("; p = malloc ( 100 ) ;", tok("; p = realloc(0, 100);")); ASSERT_EQUALS("; p = malloc ( 0 ) ;", tok("; p = realloc(0, 0);")); ASSERT_EQUALS("; free ( q ) ; p = 0 ;", tok("; p = realloc(q, 0);")); ASSERT_EQUALS("; free ( * q ) ; p = 0 ;", tok("; p = realloc(*q, 0);")); ASSERT_EQUALS("; free ( f ( z ) ) ; p = 0 ;", tok("; p = realloc(f(z), 0);")); ASSERT_EQUALS("; p = malloc ( n * m ) ;", tok("; p = realloc(0, n*m);")); ASSERT_EQUALS("; p = malloc ( f ( 1 ) ) ;", tok("; p = realloc(0, f(1));")); } void simplifyErrNoInWhile() { ASSERT_EQUALS("; while ( f ( ) ) { }", tok("; while (f() && errno == EINTR) { }")); ASSERT_EQUALS("; while ( f ( ) ) { }", tok("; while (f() && (errno == EINTR)) { }")); } void simplifyFuncInWhile() { ASSERT_EQUALS("int cppcheck:r1 = fclose ( f ) ; " "while ( cppcheck:r1 ) " "{ " "foo ( ) ; " "cppcheck:r1 = fclose ( f ) ; " "}", tok("while(fclose(f))foo();")); ASSERT_EQUALS("int cppcheck:r1 = fclose ( f ) ; " "while ( cppcheck:r1 ) " "{ " "; cppcheck:r1 = fclose ( f ) ; " "}", tok("while(fclose(f));")); ASSERT_EQUALS("int cppcheck:r1 = fclose ( f ) ; " "while ( cppcheck:r1 ) " "{ " "; cppcheck:r1 = fclose ( f ) ; " "} " "int cppcheck:r2 = fclose ( g ) ; " "while ( cppcheck:r2 ) " "{ " "; cppcheck:r2 = fclose ( g ) ; " "}", tok("while(fclose(f)); while(fclose(g));")); } void simplifyStructDecl1() { { const char code[] = "struct ABC { } abc;"; const char expected[] = "struct ABC { } ; struct ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { } * pabc;"; const char expected[] = "struct ABC { } ; struct ABC * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { } abc[4];"; const char expected[] = "struct ABC { } ; struct ABC abc [ 4 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { } abc, def;"; const char expected[] = "struct ABC { } ; struct ABC abc ; struct ABC def ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { } abc, * pabc;"; const char expected[] = "struct ABC { } ; struct ABC abc ; struct ABC * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { struct DEF {} def; } abc;"; const char expected[] = "struct ABC { struct DEF { } ; struct DEF def ; } ; struct ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { } abc;"; const char expected[] = "struct Anonymous0 { } ; struct Anonymous0 abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { } * pabc;"; const char expected[] = "struct Anonymous0 { } ; struct Anonymous0 * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { } abc[4];"; const char expected[] = "struct Anonymous0 { } ; struct Anonymous0 abc [ 4 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { } abc, def;"; const char expected[] = "struct Anonymous0 { } ; struct Anonymous0 abc ; struct Anonymous0 def ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { } abc, * pabc;"; const char expected[] = "struct Anonymous0 { } ; struct Anonymous0 abc ; struct Anonymous0 * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { struct DEF {} def; } abc;"; const char expected[] = "struct Anonymous0 { struct DEF { } ; struct DEF def ; } ; struct Anonymous0 abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { struct {} def; } abc;"; const char expected[] = "struct ABC { struct Anonymous0 { } ; struct Anonymous0 def ; } ; struct ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { struct {} def; } abc;"; const char expected[] = "struct Anonymous0 { struct Anonymous1 { } ; struct Anonymous1 def ; } ; struct Anonymous0 abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "union ABC { int i; float f; } abc;"; const char expected[] = "union ABC { int i ; float f ; } ; union ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC { struct {} def; };"; const char expected[] = "struct ABC { struct Anonymous0 { } ; struct Anonymous0 def ; } ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct ABC : public XYZ { struct {} def; };"; const char expected[] = "struct ABC : public XYZ { struct Anonymous0 { } ; struct Anonymous0 def ; } ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { int x; }; int y;"; const char expected[] = "int x ; int y ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { int x; };"; const char expected[] = "int x ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { };"; const char expected[] = ";"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { struct { struct { } ; } ; };"; const char expected[] = ";"; ASSERT_EQUALS(expected, tok(code, false)); } // ticket 2464 { const char code[] = "static struct ABC { } abc ;"; const char expected[] = "struct ABC { } ; static struct ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } // ticket #980 { const char code[] = "void f() { int A(1),B(2),C=3,D,E(5),F=6; }"; const char expected[] = "void f ( ) { int A ; A = 1 ; int B ; B = 2 ; int C ; C = 3 ; int D ; int E ; E = 5 ; int F ; F = 6 ; }"; ASSERT_EQUALS(expected, tok(code, false)); } // ticket #8284 { const char code[] = "void f() { class : foo { } abc; }"; const char expected[] = "void f ( ) { class Anonymous0 : foo < int > { } ; Anonymous0 abc ; }"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyStructDecl2() { // ticket #2479 (segmentation fault) const char code[] = "struct { char c; }"; const char expected[] = "struct { char c ; }"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyStructDecl3() { { const char code[] = "class ABC { } abc;"; const char expected[] = "class ABC { } ; ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { } * pabc;"; const char expected[] = "class ABC { } ; ABC * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { } abc[4];"; const char expected[] = "class ABC { } ; ABC abc [ 4 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { } abc, def;"; const char expected[] = "class ABC { } ; ABC abc ; ABC def ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { } abc, * pabc;"; const char expected[] = "class ABC { } ; ABC abc ; ABC * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { class DEF {} def; } abc;"; const char expected[] = "class ABC { class DEF { } ; DEF def ; } ; ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { } abc;"; const char expected[] = "class { } abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { } * pabc;"; const char expected[] = "class { } * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { } abc[4];"; const char expected[] = "class { } abc [ 4 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { } abc, def;"; const char expected[] = "class { } abc , def ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { } abc, * pabc;"; const char expected[] = "class { } abc , * pabc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct { class DEF {} def; } abc;"; const char expected[] = "struct Anonymous0 { class DEF { } ; DEF def ; } ; struct Anonymous0 abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { struct {} def; } abc;"; const char expected[] = "class ABC { struct Anonymous0 { } ; struct Anonymous0 def ; } ; ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { class {} def; } abc;"; const char expected[] = "class { class { } def ; } abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC { struct {} def; };"; const char expected[] = "class ABC { struct Anonymous0 { } ; struct Anonymous0 def ; } ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class ABC : public XYZ { struct {} def; };"; const char expected[] = "class ABC : public XYZ { struct Anonymous0 { } ; struct Anonymous0 def ; } ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { int x; }; int y;"; const char expected[] = "class { int x ; } ; int y ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { int x; };"; const char expected[] = "class { int x ; } ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { };"; const char expected[] = "class { } ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class { struct { struct { } ; } ; };"; const char expected[] = "class { } ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyStructDecl4() { const char code[] = "class ABC {\n" " void foo() {\n" " union {\n" " int i;\n" " float f;\n" " };\n" " struct Fee { } fee;\n" " }\n" " union {\n" " long long ll;\n" " double d;\n" " };\n" "} abc;\n"; const char expected[] = "class ABC { " "void foo ( ) { " "int i ; " "float & f = i ; " "struct Fee { } ; struct Fee fee ; " "} " "union { " "long long ll ; " "double d ; " "} ; " "} ; ABC abc ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyStructDecl6() { ASSERT_EQUALS("struct A { " "char integers [ X ] ; " "} ; struct A arrays ; arrays = { { 0 } } ;", tok("struct A {\n" " char integers[X];\n" "} arrays = {{0}};", false)); } void simplifyStructDecl7() { ASSERT_EQUALS("struct Anonymous0 { char x ; } ; struct Anonymous0 a [ 2 ] ;", tok("struct { char x; } a[2];", false)); ASSERT_EQUALS("struct Anonymous0 { char x ; } ; static struct Anonymous0 a [ 2 ] ;", tok("static struct { char x; } a[2];", false)); } void simplifyStructDecl8() { ASSERT_EQUALS("enum A { x , y , z } ; enum A a ; a = x ;", tok("enum A { x, y, z } a(x);", false)); ASSERT_EQUALS("enum B { x , y , z } ; enum B b ; b = x ;", tok("enum B { x , y, z } b{x};", false)); ASSERT_EQUALS("struct C { int i ; } ; struct C c ; c = { 0 } ;", tok("struct C { int i; } c{0};", false)); ASSERT_EQUALS("enum Anonymous0 { x , y , z } ; enum Anonymous0 d ; d = x ;", tok("enum { x, y, z } d(x);", false)); ASSERT_EQUALS("enum Anonymous0 { x , y , z } ; enum Anonymous0 e ; e = x ;", tok("enum { x, y, z } e{x};", false)); ASSERT_EQUALS("struct Anonymous0 { int i ; } ; struct Anonymous0 f ; f = { 0 } ;", tok("struct { int i; } f{0};", false)); ASSERT_EQUALS("enum G : short { x , y , z } ; enum G g ; g = x ;", tok("enum G : short { x, y, z } g(x);", false)); ASSERT_EQUALS("enum H : short { x , y , z } ; enum H h ; h = x ;", tok("enum H : short { x, y, z } h{x};", false)); ASSERT_EQUALS("enum class I : short { x , y , z } ; enum I i ; i = x ;", tok("enum class I : short { x, y, z } i(x);", false)); ASSERT_EQUALS("enum class J : short { x , y , z } ; enum J j ; j = x ;", tok("enum class J : short { x, y, z } j{x};", false)); } void removeUnwantedKeywords() { ASSERT_EQUALS("int var ;", tok("register int var ;", true)); ASSERT_EQUALS("short var ;", tok("register short int var ;", true)); ASSERT_EQUALS("int foo ( ) { }", tok("inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__forceinline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("constexpr int foo() { }", true)); ASSERT_EQUALS("class C { int f ( ) ; } ;", tok("class C { int f() final ; };", true)); ASSERT_EQUALS("void f ( ) { int final [ 10 ] ; }", tok("void f() { int final[10]; }", true)); ASSERT_EQUALS("int * p ;", tok("int * __restrict p;", "test.c")); ASSERT_EQUALS("int * * p ;", tok("int * __restrict__ * p;", "test.c")); ASSERT_EQUALS("void foo ( float * a , float * b ) ;", tok("void foo(float * __restrict__ a, float * __restrict__ b);", "test.c")); ASSERT_EQUALS("int * p ;", tok("int * restrict p;", "test.c")); ASSERT_EQUALS("int * * p ;", tok("int * restrict * p;", "test.c")); ASSERT_EQUALS("void foo ( float * a , float * b ) ;", tok("void foo(float * restrict a, float * restrict b);", "test.c")); ASSERT_EQUALS("int * p ;", tok("typedef int * __restrict__ rint; rint p;", "test.c")); // don't remove struct members: ASSERT_EQUALS("a = b . _inline ;", tok("a = b._inline;", true)); ASSERT_EQUALS("int i ; i = 0 ;", tok("auto int i = 0;", "test.c")); ASSERT_EQUALS("auto i ; i = 0 ;", tok("auto i = 0;", "test.cpp")); } void simplifyCallingConvention() { ASSERT_EQUALS("int f ( ) ;", tok("int __cdecl f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __stdcall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __fastcall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __clrcall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __thiscall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __syscall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __pascal f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __fortran f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __cdecl f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __stdcall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __fastcall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __clrcall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __thiscall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __syscall f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __pascal f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int __far __fortran f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int WINAPI f();", true, Settings::Win32A)); ASSERT_EQUALS("int f ( ) ;", tok("int APIENTRY f();", true, Settings::Win32A)); ASSERT_EQUALS("int f ( ) ;", tok("int CALLBACK f();", true, Settings::Win32A)); // don't simplify Microsoft defines in unix code (#7554) ASSERT_EQUALS("enum E { CALLBACK } ;", tok("enum E { CALLBACK } ;", true, Settings::Unix32)); } void simplifyFunctorCall() { ASSERT_EQUALS("IncrementFunctor ( ) ( a ) ;", tok("IncrementFunctor()(a);", true)); } // #ticket #5339 (simplify function pointer after comma) void simplifyFunctionPointer() { ASSERT_EQUALS("f ( double x , double * y ) ;", tok("f (double x, double (*y) ());", true)); } void redundant_semicolon() { ASSERT_EQUALS("void f ( ) { ; }", tok("void f() { ; }", false)); ASSERT_EQUALS("void f ( ) { ; }", tok("void f() { do { ; } while (0); }", true)); } void simplifyFunctionReturn() { { const char code[] = "typedef void (*testfp)();\n" "struct Fred\n" "{\n" " testfp get1() { return 0; }\n" " void ( * get2 ( ) ) ( ) { return 0 ; }\n" " testfp get3();\n" " void ( * get4 ( ) ) ( );\n" "};"; const char expected[] = "struct Fred " "{ " "void * get1 ( ) { return 0 ; } " "void * get2 ( ) { return 0 ; } " "void * get3 ( ) ; " "void * get4 ( ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class Fred {\n" " std::string s;\n" " const std::string & foo();\n" "};\n" "const std::string & Fred::foo() { return \"\"; }"; const char expected[] = "class Fred { " "std :: string s ; " "const std :: string & foo ( ) ; " "} ; " "const std :: string & Fred :: foo ( ) { return \"\" ; }"; ASSERT_EQUALS(expected, tok(code, false)); } { // Ticket #7916 // Tokenization would include "int fact < 2 > ( ) { return 2 > ( ) ; }" and generate // a parse error (and use after free) const char code[] = "extern \"C\" void abort ();\n" "template inline int fact2 ();\n" "template inline int fact () {\n" " return a * fact2 ();\n" "}\n" "template <> inline int fact<1> () {\n" " return 1;\n" "}\n" "template inline int fact2 () {\n" " return a * fact();\n" "}\n" "template <> inline int fact2<1> () {\n" " return 1;\n" "}\n" "int main() {\n" " fact2<3> ();\n" " fact2<2> ();\n" "}"; tok(code); } } void removeVoidFromFunction() { ASSERT_EQUALS("void foo ( ) ;", tok("void foo(void);")); } void return_strncat() { { const char code[] = "char *f()\n" "{\n" " char *temp=malloc(2);\n" " strcpy(temp,\"\");\n" " return (strncat(temp,\"a\",1));\n" "}"; ASSERT_EQUALS("char * f ( ) {" " char * temp ;" " temp = malloc ( 2 ) ;" " strcpy ( temp , \"\" ) ;" " strncat ( temp , \"a\" , 1 ) ;" " return temp ; " "}", tok(code, true)); } { const char code[] = "char *f()\n" "{\n" " char **temp=malloc(8);\n" " *temp = malloc(2);\n" " strcpy(*temp,\"\");\n" " return (strncat(*temp,\"a\",1));\n" "}"; ASSERT_EQUALS("char * f ( ) {" " char * * temp ;" " temp = malloc ( 8 ) ;" " * temp = malloc ( 2 ) ;" " strcpy ( * temp , \"\" ) ;" " strncat ( * temp , \"a\" , 1 ) ;" " return * temp ; " "}", tok(code, true)); } { const char code[] = "char *f()\n" "{\n" " char **temp=malloc(8);\n" " *temp = malloc(2);\n" " strcpy(*temp,\"\");\n" " return (strncat(temp[0],foo(b),calc(c-d)));\n" "}"; ASSERT_EQUALS("char * f ( ) {" " char * * temp ;" " temp = malloc ( 8 ) ;" " * temp = malloc ( 2 ) ;" " strcpy ( * temp , \"\" ) ;" " strncat ( temp [ 0 ] , foo ( b ) , calc ( c - d ) ) ;" " return temp [ 0 ] ; " "}", tok(code, true)); } } void removeRedundantFor() { // ticket #3069 { const char code[] = "void f() {" " for(x=0;x<1;x++) {" " y = 1;" " }" "}"; ASSERT_EQUALS("void f ( ) { { y = 1 ; } x = 1 ; }", tok(code, true)); } { const char code[] = "void f() {" " for(x=0;x<1;x++) {" " y = 1 + x;" " }" "}"; ASSERT_EQUALS("void f ( ) { x = 0 ; { y = 1 + x ; } x = 1 ; }", tok(code, true)); } { const char code[] = "void f() {" " foo();" " for(int x=0;x<1;x++) {" " y = 1 + x;" " }" "}"; ASSERT_EQUALS("void f ( ) { foo ( ) ; { int x = 0 ; y = 1 + x ; } }", tok(code, true)); } } void consecutiveBraces() { ASSERT_EQUALS("void f ( ) { }", tok("void f(){{}}", true)); ASSERT_EQUALS("void f ( ) { }", tok("void f(){{{}}}", true)); ASSERT_EQUALS("void f ( ) { for ( ; ; ) { } }", tok("void f () { for(;;){} }", true)); ASSERT_EQUALS("void f ( ) { { scope_lock lock ; foo ( ) ; } { scope_lock lock ; bar ( ) ; } }", tok("void f () { {scope_lock lock; foo();} {scope_lock lock; bar();} }", true)); } void undefinedSizeArray() { ASSERT_EQUALS("int * x ;", tok("int x [];")); ASSERT_EQUALS("int * * x ;", tok("int x [][];")); ASSERT_EQUALS("int * * x ;", tok("int * x [];")); ASSERT_EQUALS("int * * * x ;", tok("int * x [][];")); ASSERT_EQUALS("int * * * * x ;", tok("int * * x [][];")); ASSERT_EQUALS("void f ( int x [ ] , double y [ ] ) { }", tok("void f(int x[], double y[]) { }")); ASSERT_EQUALS("int x [ 13 ] = { [ 11 ] = 2 , [ 12 ] = 3 } ;", tok("int x[] = {[11]=2, [12]=3};")); } void simplifyArrayAddress() { // ticket #3304 const char code[] = "void foo() {\n" " int a[10];\n" " memset(&a[4], 0, 20*sizeof(int));\n" "}"; ASSERT_EQUALS("void foo ( ) {" " int a [ 10 ] ;" " memset ( a + 4 , 0 , 80 ) ;" " }", tok(code, true)); } void simplifyCharAt() { // ticket #4481 ASSERT_EQUALS("'h' ;", tok("\"hello\"[0] ;")); ASSERT_EQUALS("'\\n' ;", tok("\"\\n\"[0] ;")); ASSERT_EQUALS("'\\0' ;", tok("\"hello\"[5] ;")); ASSERT_EQUALS("'\\0' ;", tok("\"\"[0] ;")); ASSERT_EQUALS("'\\0' ;", tok("\"\\0\"[0] ;")); ASSERT_EQUALS("'\\n' ;", tok("\"hello\\nworld\"[5] ;")); ASSERT_EQUALS("'w' ;", tok("\"hello world\"[6] ;")); ASSERT_EQUALS("\"hello\" [ 7 ] ;", tok("\"hello\"[7] ;")); ASSERT_EQUALS("\"hello\" [ -1 ] ;", tok("\"hello\"[-1] ;")); } void test_4881() { const char code[] = "int evallex() {\n" " int c, t;\n" "again:\n" " do {\n" " if ((c = macroid(c)) == EOF_CHAR || c == '\\n') {\n" " }\n" " } while ((t = type[c]) == LET && catenate());\n" "}\n"; ASSERT_EQUALS("int evallex ( ) { int c ; int t ; again : ; do { c = macroid ( c ) ; if ( c == EOF_CHAR || c == '\\n' ) { } t = type [ c ] ; } while ( t == LET && catenate ( ) ) ; }", tok(code, true)); } void simplifyOverride() { // ticket #5069 const char code[] = "void fun() {\n" " unsigned char override[] = {0x01, 0x02};\n" " doSomething(override, sizeof(override));\n" "}\n"; ASSERT_EQUALS("void fun ( ) { char override [ 2 ] = { 1 , 2 } ; doSomething ( override , 2 ) ; }", tok(code, true)); } }; REGISTER_TEST(TestSimplifyTokens) cppcheck-1.82/test/testsimplifytypedef.cpp000066400000000000000000003673141322667425100210520ustar00rootroot00000000000000/* * 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 "platform.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" struct InternalError; class TestSimplifyTypedef : public TestFixture { public: TestSimplifyTypedef() : TestFixture("TestSimplifyTypedef") { } private: Settings settings0; Settings settings1; Settings settings2; void run() { settings0.addEnabled("style"); settings2.addEnabled("style"); TEST_CASE(simplifyTypedef1) TEST_CASE(simplifyTypedef2) TEST_CASE(simplifyTypedef3) TEST_CASE(simplifyTypedef4) TEST_CASE(simplifyTypedef5) TEST_CASE(simplifyTypedef6) TEST_CASE(simplifyTypedef7); TEST_CASE(simplifyTypedef8); TEST_CASE(simplifyTypedef9); TEST_CASE(simplifyTypedef10); TEST_CASE(simplifyTypedef11); TEST_CASE(simplifyTypedef12); TEST_CASE(simplifyTypedef13); TEST_CASE(simplifyTypedef14); TEST_CASE(simplifyTypedef15); TEST_CASE(simplifyTypedef16); TEST_CASE(simplifyTypedef17); TEST_CASE(simplifyTypedef18); // typedef vector a; TEST_CASE(simplifyTypedef19); TEST_CASE(simplifyTypedef20); TEST_CASE(simplifyTypedef21); TEST_CASE(simplifyTypedef22); TEST_CASE(simplifyTypedef23); TEST_CASE(simplifyTypedef24); TEST_CASE(simplifyTypedef25); TEST_CASE(simplifyTypedef26); TEST_CASE(simplifyTypedef27); TEST_CASE(simplifyTypedef28); TEST_CASE(simplifyTypedef29); TEST_CASE(simplifyTypedef30); TEST_CASE(simplifyTypedef31); TEST_CASE(simplifyTypedef32); TEST_CASE(simplifyTypedef33); TEST_CASE(simplifyTypedef34); // ticket #1411 TEST_CASE(simplifyTypedef35); TEST_CASE(simplifyTypedef36); // ticket #1434 TEST_CASE(simplifyTypedef37); // ticket #1449 TEST_CASE(simplifyTypedef38); TEST_CASE(simplifyTypedef39); TEST_CASE(simplifyTypedef40); TEST_CASE(simplifyTypedef43); // ticket #1588 TEST_CASE(simplifyTypedef44); TEST_CASE(simplifyTypedef45); // ticket #1613 TEST_CASE(simplifyTypedef46); TEST_CASE(simplifyTypedef47); TEST_CASE(simplifyTypedef48); // ticket #1673 TEST_CASE(simplifyTypedef49); // ticket #1691 TEST_CASE(simplifyTypedef50); TEST_CASE(simplifyTypedef51); TEST_CASE(simplifyTypedef52); // ticket #1782 TEST_CASE(simplifyTypedef54); // ticket #1814 TEST_CASE(simplifyTypedef55); TEST_CASE(simplifyTypedef56); // ticket #1829 TEST_CASE(simplifyTypedef57); // ticket #1846 TEST_CASE(simplifyTypedef58); // ticket #1963 TEST_CASE(simplifyTypedef59); // ticket #2011 TEST_CASE(simplifyTypedef60); // ticket #2035 TEST_CASE(simplifyTypedef61); // ticket #2074 and 2075 TEST_CASE(simplifyTypedef62); // ticket #2082 TEST_CASE(simplifyTypedef63); // ticket #2175 'typedef float x[3];' TEST_CASE(simplifyTypedef64); TEST_CASE(simplifyTypedef65); // ticket #2314 TEST_CASE(simplifyTypedef66); // ticket #2341 TEST_CASE(simplifyTypedef67); // ticket #2354 TEST_CASE(simplifyTypedef68); // ticket #2355 TEST_CASE(simplifyTypedef69); // ticket #2348 TEST_CASE(simplifyTypedef70); // ticket #2348 TEST_CASE(simplifyTypedef71); // ticket #2348 TEST_CASE(simplifyTypedef72); // ticket #2375 TEST_CASE(simplifyTypedef73); // ticket #2412 TEST_CASE(simplifyTypedef74); // ticket #2414 TEST_CASE(simplifyTypedef75); // ticket #2426 TEST_CASE(simplifyTypedef76); // ticket #2453 TEST_CASE(simplifyTypedef77); // ticket #2554 TEST_CASE(simplifyTypedef78); // ticket #2568 TEST_CASE(simplifyTypedef79); // ticket #2348 TEST_CASE(simplifyTypedef80); // ticket #2587 TEST_CASE(simplifyTypedef81); // ticket #2603 TEST_CASE(simplifyTypedef82); // ticket #2403 TEST_CASE(simplifyTypedef83); // ticket #2620 TEST_CASE(simplifyTypedef84); // ticket #2630 TEST_CASE(simplifyTypedef85); // ticket #2651 TEST_CASE(simplifyTypedef86); // ticket #2581 TEST_CASE(simplifyTypedef87); // ticket #2651 TEST_CASE(simplifyTypedef88); // ticket #2675 TEST_CASE(simplifyTypedef89); // ticket #2717 TEST_CASE(simplifyTypedef90); // ticket #2718 TEST_CASE(simplifyTypedef91); // ticket #2716 TEST_CASE(simplifyTypedef92); // ticket #2736 TEST_CASE(simplifyTypedef93); // ticket #2738 TEST_CASE(simplifyTypedef94); // ticket #1982 TEST_CASE(simplifyTypedef95); // ticket #2844 TEST_CASE(simplifyTypedef96); // ticket #2886 TEST_CASE(simplifyTypedef97); // ticket #2983 (segmentation fault) TEST_CASE(simplifyTypedef99); // ticket #2999 TEST_CASE(simplifyTypedef100); // ticket #3000 TEST_CASE(simplifyTypedef101); // ticket #3003 (segmentation fault) TEST_CASE(simplifyTypedef102); // ticket #3004 TEST_CASE(simplifyTypedef103); // ticket #3007 TEST_CASE(simplifyTypedef104); // ticket #3070 TEST_CASE(simplifyTypedef105); // ticket #3616 TEST_CASE(simplifyTypedef106); // ticket #3619 TEST_CASE(simplifyTypedef107); // ticket #3963 - bad code => segmentation fault TEST_CASE(simplifyTypedef108); // ticket #4777 TEST_CASE(simplifyTypedef109); // ticket #1823 - rvalue reference TEST_CASE(simplifyTypedef110); // ticket #6268 TEST_CASE(simplifyTypedef111); // ticket #6345 TEST_CASE(simplifyTypedef112); // ticket #6048 TEST_CASE(simplifyTypedef113); // ticket #7030 TEST_CASE(simplifyTypedef114); // ticket #7058 - skip "struct", AB::.. TEST_CASE(simplifyTypedef115); // ticket #6998 TEST_CASE(simplifyTypedef116); // ticket #5624 TEST_CASE(simplifyTypedef117); // ticket #6507 TEST_CASE(simplifyTypedef118); // ticket #5749 TEST_CASE(simplifyTypedef119); // ticket #7541 TEST_CASE(simplifyTypedefFunction1); TEST_CASE(simplifyTypedefFunction2); // ticket #1685 TEST_CASE(simplifyTypedefFunction3); TEST_CASE(simplifyTypedefFunction4); TEST_CASE(simplifyTypedefFunction5); TEST_CASE(simplifyTypedefFunction6); TEST_CASE(simplifyTypedefFunction7); TEST_CASE(simplifyTypedefFunction8); TEST_CASE(simplifyTypedefFunction9); TEST_CASE(simplifyTypedefFunction10); // #5191 TEST_CASE(simplifyTypedefShadow); // #4445 - shadow variable } std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) { errout.str(""); settings0.inconclusive = true; settings0.debugwarnings = debugwarnings; // show warnings about unhandled typedef settings0.platform(type); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); if (simplify) tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, !simplify); } std::string simplifyTypedef(const char code[]) { errout.str(""); Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.list.createTokens(istr); tokenizer.createLinks(); tokenizer.simplifyTypedef(); return tokenizer.tokens()->stringifyList(0, false); } void checkSimplifyTypedef(const char code[]) { errout.str(""); // Tokenize.. settings2.inconclusive = true; settings2.debugwarnings = true; // show warnings about unhandled typedef Tokenizer tokenizer(&settings2, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); } void simplifyTypedef1() { const char code[] = "class A\n" "{\n" "public:\n" " typedef wchar_t duplicate;\n" " void foo() {}\n" "};\n" "typedef A duplicate;\n" "int main()\n" "{\n" " duplicate a;\n" " a.foo();\n" " A::duplicate c = 0;\n" "}"; const char expected[] = "class A " "{ " "public: " "" "void foo ( ) { } " "} ; " "int main ( ) " "{ " "A a ; " "a . foo ( ) ; " "wchar_t c ; c = 0 ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef2() { const char code[] = "class A;\n" "typedef A duplicate;\n" "class A\n" "{\n" "public:\n" "typedef wchar_t duplicate;\n" "duplicate foo() { wchar_t b; return b; }\n" "};"; const char expected[] = "class A ; " "class A " "{ " "public: " "" "wchar_t foo ( ) { wchar_t b ; return b ; } " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef3() { const char code[] = "class A {};\n" "typedef A duplicate;\n" "wchar_t foo()\n" "{\n" "typedef wchar_t duplicate;\n" "duplicate b;\n" "return b;\n" "}\n" "int main()\n" "{\n" "duplicate b;\n" "}"; const char expected[] = "class A { } ; " "wchar_t foo ( ) " "{ " "" "wchar_t b ; " "return b ; " "} " "int main ( ) " "{ " "A b ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef4() { const char code[] = "typedef int s32;\n" "typedef unsigned int u32;\n" "void f()\n" "{\n" " s32 ivar = -2;\n" " u32 uvar = 2;\n" " return uvar / ivar;\n" "}"; const char expected[] = "void f ( ) " "{ " "int ivar ; ivar = -2 ; " "unsigned int uvar ; uvar = 2 ; " "return uvar / ivar ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef5() { // ticket #780 const char code[] = "typedef struct yy_buffer_state *YY_BUFFER_STATE;\n" "void f()\n" "{\n" " YY_BUFFER_STATE state;\n" "}"; const char expected[] = "void f ( ) " "{ " "struct yy_buffer_state * state ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef6() { // ticket #983 const char code[] = "namespace VL {\n" " typedef float float_t ;\n" " inline VL::float_t fast_atan2(VL::float_t y, VL::float_t x){}\n" "}"; const char expected[] = "namespace VL { " "" "float fast_atan2 ( float y , float x ) { } " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef7() { const char code[] = "typedef int abc ; " "Fred :: abc f ;"; const char expected[] = "Fred :: abc f ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef8() { const char code[] = "typedef int INT;\n" "typedef unsigned int UINT;\n" "typedef int * PINT;\n" "typedef unsigned int * PUINT;\n" "typedef int & RINT;\n" "typedef unsigned int & RUINT;\n" "typedef const int & RCINT;\n" "typedef const unsigned int & RCUINT;\n" "INT ti;\n" "UINT tui;\n" "PINT tpi;\n" "PUINT tpui;\n" "RINT tri;\n" "RUINT trui;\n" "RCINT trci;\n" "RCUINT trcui;"; const char expected[] = "int ti ; " "unsigned int tui ; " "int * tpi ; " "unsigned int * tpui ; " "int & tri ; " "unsigned int & trui ; " "const int & trci ; " "const unsigned int & trcui ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef9() { const char code[] = "typedef struct s S, * PS;\n" "typedef struct t { int a; } T, *TP;\n" "typedef struct { int a; } U;\n" "typedef struct { int a; } * V;\n" "S s;\n" "PS ps;\n" "T t;\n" "TP tp;\n" "U u;\n" "V v;"; const char expected[] = "struct t { int a ; } ; " "struct U { int a ; } ; " "struct Unnamed0 { int a ; } ; " "struct s s ; " "struct s * ps ; " "struct t t ; " "struct t * tp ; " "struct U u ; " "struct Unnamed0 * v ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef10() { const char code[] = "typedef union s S, * PS;\n" "typedef union t { int a; float b ; } T, *TP;\n" "typedef union { int a; float b; } U;\n" "typedef union { int a; float b; } * V;\n" "S s;\n" "PS ps;\n" "T t;\n" "TP tp;\n" "U u;\n" "V v;"; const char expected[] = "union t { int a ; float b ; } ; " "union U { int a ; float b ; } ; " "union Unnamed1 { int a ; float b ; } ; " "union s s ; " "union s * ps ; " "union t t ; " "union t * tp ; " "union U u ; " "union Unnamed1 * v ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef11() { const char code[] = "typedef enum { a = 0 , b = 1 , c = 2 } abc;\n" "typedef enum xyz { x = 0 , y = 1 , z = 2 } XYZ;\n" "abc e1;\n" "XYZ e2;"; const char expected[] = "enum abc { a = 0 , b = 1 , c = 2 } ; " "enum xyz { x = 0 , y = 1 , z = 2 } ; " "abc e1 ; " "enum xyz e2 ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef12() { const char code[] = "typedef vector V1;\n" "typedef std::vector V2;\n" "typedef std::vector > V3;\n" "typedef std::list::iterator IntListIterator;\n" "V1 v1;\n" "V2 v2;\n" "V3 v3;\n" "IntListIterator iter;"; const char expected[] = "vector < int > v1 ; " "std :: vector < int > v2 ; " "std :: vector < std :: vector < int > > v3 ; " "std :: list < int > :: iterator iter ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef13() { // ticket # 1167 const char code[] = "typedef std::pair Func;" "typedef std::vector CallQueue;" "int main() {}"; // Tokenize and check output.. tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef14() { // ticket # 1232 const char code[] = "template struct E" "{" " typedef E0)?(N-1):0> v;" " typedef typename add::val val;" " FP_M(val);" "};" "template struct E " "{" " typedef typename D<1>::val val;" " FP_M(val);" "};"; // Tokenize and check output.. tok(code, true, Settings::Native, false); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef15() { { const char code[] = "typedef char frame[10];\n" "frame f;"; const char expected[] = "char f [ 10 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "typedef unsigned char frame[10];\n" "frame f;"; const char expected[] = "unsigned char f [ 10 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyTypedef16() { // ticket # 1252 const char code[] = "typedef char MOT8;\n" "typedef MOT8 CHFOO[4096];\n" "typedef struct {\n" " CHFOO freem;\n" "} STRFOO;"; // Tokenize and check output.. tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef17() { const char code[] = "typedef char * PCHAR, CHAR;\n" "PCHAR pc;\n" "CHAR c;"; const char expected[] = "char * pc ; " "char c ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef18() { const char code[] = "typedef vector a;\n" "a b;"; ASSERT_EQUALS("vector < int [ 4 ] > b ;", tok(code)); } void simplifyTypedef19() { { // ticket #1275 const char code[] = "typedef struct {} A, *B, **C;\n" "A a;\n" "B b;\n" "C c;"; const char expected[] = "struct A { } ; " "struct A a ; " "struct A * b ; " "struct A * * c ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "typedef struct {} A, *********B;\n" "A a;\n" "B b;"; const char expected[] = "struct A { } ; " "struct A a ; " "struct A * * * * * * * * * b ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "typedef struct {} **********A, *B, C;\n" "A a;\n" "B b;\n" "C c;"; const char expected[] = "struct Unnamed2 { } ; " "struct Unnamed2 * * * * * * * * * * a ; " "struct Unnamed2 * b ; " "struct Unnamed2 c ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyTypedef20() { // ticket #1284 const char code[] = "typedef jobject invoke_t (jobject, Proxy *, Method *, JArray< jobject > *);"; ASSERT_EQUALS(";", tok(code)); } void simplifyTypedef21() { const char code[] = "typedef void (* PF)();\n" "typedef void * (* PFV)(void *);\n" "PF pf;\n" "PFV pfv;"; const char expected[] = "" "" "void ( * pf ) ( ) ; " "void * ( * pfv ) ( void * ) ;"; ASSERT_EQUALS(expected, simplifyTypedef(code)); } void simplifyTypedef22() { { const char code[] = "class Fred {\n" " typedef void (*testfp)();\n" " testfp get() { return test; }\n" " static void test() { }\n" "};"; const char expected[] = "class Fred { " "" "void * get ( ) { return test ; } " "static void test ( ) { } " "} ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class Fred {\n" " typedef void * (*testfp)(void *);\n" " testfp get() { return test; }\n" " static void * test(void * p) { return p; }\n" "};"; const char expected[] = "class Fred { " "" "void * * get ( ) { return test ; } " "static void * test ( void * p ) { return p ; } " "} ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class Fred {\n" " typedef unsigned int * (*testfp)(unsigned int *);\n" " testfp get() { return test; }\n" " static unsigned int * test(unsigned int * p) { return p; }\n" "};"; const char expected[] = "class Fred { " "" "unsigned int * * get ( ) { return test ; } " "static unsigned int * test ( unsigned int * p ) { return p ; } " "} ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class Fred {\n" " typedef const unsigned int * (*testfp)(const unsigned int *);\n" " testfp get() { return test; }\n" " static const unsigned int * test(const unsigned int * p) { return p; }\n" "};"; // static const gets changed to const static const char expected[] = "class Fred { " "" "const unsigned int * * get ( ) { return test ; } " "static const unsigned int * test ( const unsigned int * p ) { return p ; } " "} ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "class Fred {\n" " typedef void * (*testfp)(void *);\n" " testfp get(int i) { return test; }\n" " static void * test(void * p) { return p; }\n" "};"; const char expected[] = "class Fred { " "" "void * * get ( int i ) { return test ; } " "static void * test ( void * p ) { return p ; } " "} ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyTypedef23() { const char code[] = "typedef bool (*Callback) (int i);\n" "void addCallback(Callback callback) { }\n" "void addCallback1(Callback callback, int j) { }"; const char expected[] = "void addCallback ( bool * callback ) { } " "void addCallback1 ( bool * callback , int j ) { }"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef24() { { const char code[] = "typedef int (*fp)();\n" "void g( fp f )\n" "{\n" " fp f2 = (fp)f;\n" "}"; const char expected[] = "void g ( int * f ) " "{ " "int * f2 ; f2 = ( int * ) f ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "typedef int (*fp)();\n" "void g( fp f )\n" "{\n" " fp f2 = static_cast(f);\n" "}"; const char expected[] = "void g ( int * f ) " "{ " "int * f2 ; f2 = static_cast < int * > ( f ) ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyTypedef25() { { // ticket #1298 const char code[] = "typedef void (*fill_names_f) (const char *);\n" "struct vfs_class {\n" " void (*fill_names) (struct vfs_class *me, fill_names_f);\n" "}"; const char expected[] = "struct vfs_class { " "void ( * fill_names ) ( struct vfs_class * me , void ( * ) ( const char * ) ) ; " "}"; ASSERT_EQUALS(expected, simplifyTypedef(code)); } { const char code[] = "typedef void (*fill_names_f) (const char *);\n" "struct vfs_class {\n" " void (*fill_names) (fill_names_f, struct vfs_class *me);\n" "}"; const char expected[] = "struct vfs_class { " "void ( * fill_names ) ( void ( * ) ( const char * ) , struct vfs_class * me ) ; " "}"; ASSERT_EQUALS(expected, simplifyTypedef(code)); } } void simplifyTypedef26() { { const char code[] = "typedef void (*Callback) ();\n" "void addCallback(Callback (*callback)());"; const char expected[] = "void addCallback ( void ( * ( * callback ) ( ) ) ( ) ) ;"; ASSERT_EQUALS(expected, tok(code, false)); } { // ticket # 1307 const char code[] = "typedef void (*pc_video_update_proc)(bitmap_t *bitmap,\n" "struct mscrtc6845 *crtc);\n" "\n" "struct mscrtc6845 *pc_video_start(pc_video_update_proc (*choosevideomode)(running_machine *machine, int *width, int *height, struct mscrtc6845 *crtc));"; const char expected[] = "struct mscrtc6845 * pc_video_start ( void ( * ( * choosevideomode ) ( running_machine * machine , int * width , int * height , struct mscrtc6845 * crtc ) ) ( bitmap_t * bitmap , struct mscrtc6845 * crtc ) ) ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyTypedef27() { // ticket #1316 const char code[] = "int main()\n" "{\n" " typedef int (*func_ptr)(float, double);\n" " VERIFY((is_same::type, int>::value));\n" "}"; const char expected[] = "int main ( ) " "{ " "" "VERIFY ( is_same < result_of < int ( * ( char , float ) ) ( float , double ) > :: type , int > :: value ) ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef28() { const char code[] = "typedef std::pair (*F)(double);\n" "F f;"; const char expected[] = "std :: pair < double , double > ( * f ) ( double ) ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef29() { const char code[] = "typedef int array [ice_or::value, is_int::value>::value ? 1 : -1];\n" "typedef int array1 [N];\n" "typedef int array2 [N][M];\n" "typedef int int_t, int_array[N];\n" "array a;\n" "array1 a1;\n" "array2 a2;\n" "int_t t;\n" "int_array ia;"; const char expected[] = "int a [ ice_or < is_int < int > :: value , is_int < UDT > :: value > :: value ? 1 : -1 ] ; " "int a1 [ N ] ; " "int a2 [ N ] [ M ] ; " "int t ; " "int ia [ N ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef30() { const char code[] = "typedef ::std::list int_list;\n" "typedef ::std::list::iterator int_list_iterator;\n" "typedef ::std::list int_list_array[10];\n" "int_list il;\n" "int_list_iterator ili;\n" "int_list_array ila;"; const char expected[] = ":: std :: list < int > il ; " ":: std :: list < int > :: iterator ili ; " ":: std :: list < int > ila [ 10 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef31() { { const char code[] = "class A {\n" "public:\n" " typedef int INT;\n" " INT get() const;\n" " void put(INT x) { a = x; }\n" " INT a;\n" "};\n" "A::INT A::get() const { return a; }\n" "A::INT i = A::a;"; const char expected[] = "class A { " "public: " "" "int get ( ) const ; " "void put ( int x ) { a = x ; } " "int a ; " "} ; " "int A :: get ( ) const { return a ; } " "int i ; i = A :: a ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct A {\n" " typedef int INT;\n" " INT get() const;\n" " void put(INT x) { a = x; }\n" " INT a;\n" "};\n" "A::INT A::get() const { return a; }\n" "A::INT i = A::a;"; const char expected[] = "struct A { " "" "int get ( ) const ; " "void put ( int x ) { a = x ; } " "int a ; " "} ; " "int A :: get ( ) const { return a ; } " "int i ; i = A :: a ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void simplifyTypedef32() { const char code[] = "typedef char CHAR;\n" "typedef CHAR * LPSTR;\n" "typedef const CHAR * LPCSTR;\n" "CHAR c;\n" "LPSTR cp;\n" "LPCSTR ccp;"; const char expected[] = "char c ; " "char * cp ; " "const char * ccp ;"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef33() { const char code[] = "class A {\n" "public:\n" " typedef char CHAR_A;\n" " CHAR_A funA();\n" " class B {\n" " public:\n" " typedef short SHRT_B;\n" " SHRT_B funB();\n" " class C {\n" " public:\n" " typedef int INT_C;\n" " INT_C funC();\n" " struct D {\n" " typedef long LONG_D;\n" " LONG_D funD();\n" " LONG_D d;\n" " };\n" " INT_C c;\n" " };\n" " SHRT_B b;\n" " };\n" " CHAR_A a;\n" "};\n" "A::CHAR_A A::funA() { return a; }\n" "A::B::SHRT_B A::B::funB() { return b; }\n" "A::B::C::INT_C A::B::C::funC() { return c; }" "A::B::C::D::LONG_D A::B::C::D::funD() { return d; }"; const char codeFullSpecified[] = "class A {\n" "public:\n" " typedef char CHAR_A;\n" " A::CHAR_A funA();\n" " class B {\n" " public:\n" " typedef short SHRT_B;\n" " A::B::SHRT_B funB();\n" " class C {\n" " public:\n" " typedef int INT_C;\n" " A::B::C::INT_C funC();\n" " struct D {\n" " typedef long LONG_D;\n" " A::B::C::D::LONG_D funD();\n" " A::B::C::D::LONG_D d;\n" " };\n" " A::B::C::INT_C c;\n" " };\n" " A::B::SHRT_B b;\n" " };\n" " A::CHAR_A a;\n" "};\n" "A::CHAR_A A::funA() { return a; }\n" "A::B::SHRT_B A::B::funB() { return b; }\n" "A::B::C::INT_C A::B::C::funC() { return c; }" "A::B::C::D::LONG_D A::B::C::D::funD() { return d; }"; const char codePartialSpecified[] = "class A {\n" "public:\n" " typedef char CHAR_A;\n" " CHAR_A funA();\n" " class B {\n" " public:\n" " typedef short SHRT_B;\n" " B::SHRT_B funB();\n" " class C {\n" " public:\n" " typedef int INT_C;\n" " C::INT_C funC();\n" " struct D {\n" " typedef long LONG_D;\n" " D::LONG_D funD();\n" " C::D::LONG_D d;\n" " };\n" " B::C::INT_C c;\n" " };\n" " B::SHRT_B b;\n" " };\n" " CHAR_A a;\n" "};\n" "A::CHAR_A A::funA() { return a; }\n" "A::B::SHRT_B A::B::funB() { return b; }\n" "A::B::C::INT_C A::B::C::funC() { return c; }" "A::B::C::D::LONG_D A::B::C::D::funD() { return d; }"; const char expected[] = "class A { " "public: " "" "char funA ( ) ; " "class B { " "public: " "" "short funB ( ) ; " "class C { " "public: " "" "int funC ( ) ; " "struct D { " "" "long funD ( ) ; " "long d ; " "} ; " "int c ; " "} ; " "short b ; " "} ; " "char a ; " "} ; " "char A :: funA ( ) { return a ; } " "short A :: B :: funB ( ) { return b ; } " "int A :: B :: C :: funC ( ) { return c ; } " "long A :: B :: C :: D :: funD ( ) { return d ; }"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS(expected, tok(codePartialSpecified, false)); ASSERT_EQUALS(expected, tok(codeFullSpecified, false)); } void simplifyTypedef34() { // ticket #1411 const char code[] = "class X { };\n" "typedef X (*foofunc)(const X&);\n" "int main()\n" "{\n" " foofunc *Foo = new foofunc[2];\n" "}"; const char expected[] = "class X { } ; " "int main ( ) " "{ " "X * * Foo ; Foo = new X ( * ) ( const X & ) [ 2 ] ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef35() { const char code[] = "typedef int A;\n" "class S\n" "{\n" "public:\n" " typedef float A;\n" " A a;\n" " virtual void fun(A x);\n" "};\n" "void S::fun(S::A) { };\n" "class S1 : public S\n" "{\n" "public:\n" " void fun(S::A) { }\n" "};\n" "struct T\n" "{\n" " typedef A B;\n" " B b;\n" "};\n" "float fun1(float A) { return A; }\n" "float fun2(float a) { float A = a++; return A; }\n" "float fun3(int a)\n" "{\n" " typedef struct { int a; } A;\n" " A s; s.a = a;\n" " return s.a;\n" "}\n" "int main()\n" "{\n" " A a = 0;\n" " S::A s = fun1(a) + fun2(a) - fun3(a);\n" " return a + s;\n" "}"; const char expected[] = "class S " "{ " "public: " "" "float a ; " "virtual void fun ( float x ) ; " "} ; " "void S :: fun ( float ) { } ; " "class S1 : public S " "{ " "public: " "void fun ( float ) { } " "} ; " "struct T " "{ " "" "int b ; " "} ; " "float fun1 ( float A ) { return A ; } " "float fun2 ( float a ) { float A ; A = a ++ ; return A ; } " "float fun3 ( int a ) " "{ " "struct A { int a ; } ; " "struct A s ; s . a = a ; " "return s . a ; " "} " "int main ( ) " "{ " "int a ; a = 0 ; " "float s ; s = fun1 ( a ) + fun2 ( a ) - fun3 ( a ) ; " "return a + s ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:28]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n", errout.str()); } void simplifyTypedef36() { // ticket #1434 const char code[] = "typedef void (*TIFFFaxFillFunc)();\n" "void f(va_list ap)\n" "{\n" " *va_arg(ap, TIFFFaxFillFunc*) = 0;\n" "}"; const char expected[] = "void f ( va_list ap ) " "{ " "* va_arg ( ap , void ( * * ) ( ) ) = 0 ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef37() { const char code[] = "typedef int INT;\n" "void f()\n" "{\n" " INT i; { }\n" "}"; const char expected[] = "void f ( ) " "{ " "int i ; { } " "}"; ASSERT_EQUALS(expected, tok(code, false)); } void simplifyTypedef38() { const char code[] = "typedef C A;\n" "struct AB : public A, public B { };"; const char expected[] = "struct AB : public C , public B { } ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef39() { const char code[] = "typedef int A;\n" "template struct S{};"; const char expected[] = "template < const int , int > struct S { } ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef40() { const char code[] = "typedef int A;\n" "typedef int B;\n" "template class C { };"; const char expected[] = "template < class A , class B > class C { } ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef43() { // ticket #1588 { const char code[] = "typedef struct foo A;\n" "struct A\n" "{\n" " int alloclen;\n" "};"; // The expected result.. const char expected[] = "struct A " "{ " "int alloclen ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef union foo A;\n" "union A\n" "{\n" " int alloclen;\n" "};"; // The expected result.. const char expected[] = "union A " "{ " "int alloclen ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef class foo A;\n" "class A\n" "{\n" " int alloclen;\n" "};"; // The expected result.. const char expected[] = "class A " "{ " "int alloclen ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef44() { { const char code[] = "typedef std::map Map;\n" "class MyMap : public Map\n" "{\n" "};"; // The expected result.. const char expected[] = "class MyMap : public std :: map < std :: string , int > " "{ " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef std::map Map;\n" "class MyMap : protected Map\n" "{\n" "};"; // The expected result.. const char expected[] = "class MyMap : protected std :: map < std :: string , int > " "{ " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef std::map Map;\n" "class MyMap : private Map\n" "{\n" "};"; // The expected result.. const char expected[] = "class MyMap : private std :: map < std :: string , int > " "{ " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef struct foo { } A;\n" "struct MyA : public A\n" "{\n" "};"; // The expected result.. const char expected[] = "struct foo { } ; " "struct MyA : public foo " "{ " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef class foo { } A;\n" "class MyA : public A\n" "{\n" "};"; // The expected result.. const char expected[] = "class foo { } ; " "class MyA : public foo " "{ " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef45() { // ticket # 1613 const char code[] = "void fn() {\n" " typedef foo<> bar;\n" " while (0 > bar(1)) {}\n" "}"; checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef46() { const char code[] = "typedef const struct A { int a; } * AP;\n" "AP ap;"; // The expected result.. const char expected[] = "struct A { int a ; } ; " "const struct A * ap ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef47() { { const char code[] = "typedef std::pair const I;\n" "I i;"; // The expected result.. const char expected[] = "std :: pair < int , int > const i ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "typedef void (X:: *F)();\n" "F f;"; // The expected result.. const char expected[] = "void * f ;"; ASSERT_EQUALS(expected, tok(code)); } } void simplifyTypedef48() { // ticket #1673 const char code[] = "typedef struct string { } string;\n" "void foo (LIST *module_name)\n" "{\n" " bar(module_name ? module_name->string : 0);\n" "}"; // The expected result.. const char expected[] = "struct string { } ; " "void foo ( LIST * module_name ) " "{ " "bar ( module_name ? module_name . string : 0 ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef49() { // ticket #1691 const char code[] = "class Class2 {\n" "typedef const Class & Const_Reference;\n" "void some_method (Const_Reference x) const {}\n" "void another_method (Const_Reference x) const {}\n" "}"; // The expected result.. const char expected[] = "class Class2 { " "" "void some_method ( const Class & x ) const { } " "void another_method ( const Class & x ) const { } " "}"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef50() { const char code[] = "typedef char (* type1)[10];\n" "typedef char (& type2)[10];\n" "typedef char (& type3)[x];\n" "typedef char (& type4)[x + 2];\n" "type1 t1;\n" "type1 (*tp1)[2];\n" "type2 t2;\n" "type3 t3;\n" "type4 t4;"; // The expected result.. const char expected[] = "char ( * t1 ) [ 10 ] ; " "char ( * ( * tp1 ) [ 2 ] ) [ 10 ] ; " "char ( & t2 ) [ 10 ] ; " "char ( & t3 ) [ x ] ; " "char ( & t4 ) [ x + 2 ] ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef51() { const char code[] = "class A { public: int i; };\n" "typedef const char (A :: * type1);\n" "type1 t1 = &A::i;"; // The expected result.. const char expected[] = "class A { public: int i ; } ; " "const char ( A :: * t1 ) ; t1 = & A :: i ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef52() { // ticket #1782 { const char code[] = "typedef char (* type1)[10];\n" "type1 foo() { }"; // The expected result.. const char expected[] = "char ( * foo ( ) ) [ 10 ] { }"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef char (* type1)[10];\n" "LOCAL(type1) foo() { }"; // this is invalid C so just make sure it doesn't generate an internal error checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef54() { // ticket #1814 const char code[] = "void foo()\n" "{\n" " typedef std::basic_string string_type;\n" " try\n" " {\n" " throw string_type(\"leak\");\n" " }\n" " catch (const string_type&)\n" " {\n" " pthread_exit (0);\n" " }\n" "}"; checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef55() { const char code[] = "typedef volatile unsigned long * const hwreg_t ;\n" "typedef void *const t1[2];\n" "typedef int*const *_Iterator;\n" "hwreg_t v1;\n" "t1 v2;\n" "_Iterator v3;"; // The expected result.. const char expected[] = "long * const v1 ; " "void * const v2 [ 2 ] ; " "int * const * v3 ;"; ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef56() { // ticket #1829 const char code[] = "struct C {\n" " typedef void (*fptr)();\n" " const fptr pr;\n" " operator const fptr& () { return pr; }\n" "};"; // The expected result.. const char expected[] = "struct C { " "" "const void * pr ; " // this gets simplified to a regular pointer "operatorconstvoid(*)()& ( ) { return pr ; } " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef57() { // ticket #1846 const char code[] = "void foo() {\n" " typedef int A;\n" " A a = A(1) * A(2);\n" "};"; // The expected result.. const char expected[] = "void foo ( ) { " "" "int a ; a = int ( 1 ) * int ( 2 ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef58() { // ticket #1963 { const char code[] = "typedef int vec2_t[2];\n" "vec2_t coords[4] = {1,2,3,4,5,6,7,8};"; // The expected result.. const char expected[] = "int coords [ 4 ] [ 2 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef int vec2_t[2];\n" "vec2_t coords[4][5][6+1] = {1,2,3,4,5,6,7,8};"; // The expected result.. const char expected[] = "int coords [ 4 ] [ 5 ] [ 7 ] [ 2 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef59() { // ticket #2011 const char code[] = "template class SomeTemplateClass {\n" " typedef void (SomeTemplateClass::*MessageDispatcherFunc)(SerialInputMessage&);\n" "};"; // The expected result.. const char expected[] = "template < typename DISPATCHER > class SomeTemplateClass { } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef60() { // ticket #2035 const char code[] = "typedef enum {qfalse, qtrue} qboolean;\n" "typedef qboolean (*localEntitiyAddFunc_t) (struct le_s * le, entity_t * ent);\n" "void f()\n" "{\n" " qboolean b;\n" " localEntitiyAddFunc_t f;\n" "}"; // The expected result.. const char expected[] = "enum qboolean { qfalse , qtrue } ; void f ( ) { qboolean b ; qboolean * f ; }"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef61() { // ticket #2074 and 2075 const char code1[] = "typedef unsigned char (*Mf_GetIndexByte_Func) (void);\n" "typedef const unsigned char * (*Mf_GetPointerToCurrentPos_Func)(void);"; // Check for output.. checkSimplifyTypedef(code1); ASSERT_EQUALS("", errout.str()); const char code2[] = "typedef unsigned long uint32_t;\n" "typedef uint32_t (*write_type_t) (uint32_t);"; // Check for output.. checkSimplifyTypedef(code2); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef62() { // ticket #2082 const char code1[] = "typedef char TString[256];\n" "void f()\n" "{\n" " TString a, b;\n" "}"; // The expected tokens.. const char expected1[] = "void f ( ) { char a [ 256 ] ; char b [ 256 ] ; }"; ASSERT_EQUALS(expected1, tok(code1, false)); ASSERT_EQUALS("", errout.str()); const char code2[] = "typedef char TString[256];\n" "void f()\n" "{\n" " TString a = { 0 }, b = { 0 };\n" "}"; // The expected tokens.. const char expected2[] = "void f ( ) { char a [ 256 ] ; a = { 0 } ; char b [ 256 ] ; b = { 0 } ; }"; ASSERT_EQUALS(expected2, tok(code2, false)); ASSERT_EQUALS("", errout.str()); const char code3[] = "typedef char TString[256];\n" "void f()\n" "{\n" " TString a = \"\", b = \"\";\n" "}"; // The expected tokens.. const char expected3[] = "void f ( ) { char a [ 256 ] ; a = \"\" ; char b [ 256 ] ; b = \"\" ; }"; ASSERT_EQUALS(expected3, tok(code3, false)); ASSERT_EQUALS("", errout.str()); const char code4[] = "typedef char TString[256];\n" "void f()\n" "{\n" " TString a = \"1234\", b = \"5678\";\n" "}"; // The expected tokens.. const char expected4[] = "void f ( ) { char a [ 256 ] ; a = \"1234\" ; char b [ 256 ] ; b = \"5678\" ; }"; ASSERT_EQUALS(expected4, tok(code4, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef63() { // ticket #2175 'typedef float x[3];' const char code[] = "typedef float x[3];\n" "x a,b,c;"; const std::string actual(tok(code)); ASSERT_EQUALS("float a [ 3 ] ; float b [ 3 ] ; float c [ 3 ] ;", actual); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef64() { const char code[] = "typedef __typeof__(__type1() + __type2()) __type;" "__type t;"; const std::string actual(tok(code)); ASSERT_EQUALS("__typeof__ ( __type1 ( ) + __type2 ( ) ) t ;", actual); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef65() { // ticket #2314 const char code[] = "typedef BAR Foo;\n" "int main() {\n" " Foo b(0);\n" " return b > Foo(10);\n" "}"; const std::string actual(tok(code, true, Settings::Native, false)); ASSERT_EQUALS("int main ( ) { BAR < int > b ( 0 ) ; return b > BAR < int > ( 10 ) ; }", actual); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef66() { // ticket #2341 const char code[] = "typedef long* GEN;\n" "extern GEN (*foo)(long);"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef67() { // ticket #2354 const char code[] = "typedef int ( * Function ) ( ) ;\n" "void f ( ) {\n" " ((Function * (*) (char *, char *, int, int)) global[6]) ( \"assoc\", \"eggdrop\", 106, 0);\n" "}"; const char expected[] = "void f ( ) { " "( ( int ( * * ( * ) ( char * , char * , int , int ) ) ( ) ) global [ 6 ] ) ( \"assoc\" , \"eggdrop\" , 106 , 0 ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef68() { // ticket #2355 const char code[] = "typedef FMAC1 void (* a) ();\n" "void *(*b) ();"; const std::string actual(tok(code)); ASSERT_EQUALS("void * * b ;", actual); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef69() { // ticket #2348 const char code[] = "typedef int (*CompilerHook)();\n" "typedef struct VirtualMachine\n" "{\n" " CompilerHook *(*compilerHookVector)(void);\n" "}VirtualMachine;"; const char expected[] = "struct VirtualMachine " "{ " "int ( * * ( * compilerHookVector ) ( void ) ) ( ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef70() { // ticket #2348 const char code[] = "typedef int pread_f ( int ) ;\n" "pread_f *(*test_func)(char *filename);"; const char expected[] = "int ( * ( * test_func ) ( char * filename ) ) ( int ) ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef71() { // ticket #2348 { const char code[] = "typedef int RexxFunctionHandler();\n" "RexxFunctionHandler *(efuncs[1]);"; const char expected[] = "int ( * ( efuncs [ 1 ] ) ) ( ) ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef int RexxFunctionHandler();\n" "RexxFunctionHandler *(efuncs[]) = { NULL, NULL };"; const char expected[] = "int ( * ( efuncs [ ] ) ) ( ) = { NULL , NULL } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef72() { // ticket #2374 // inline operator { const char code[] = "class Fred {\n" " typedef int* (Fred::*F);\n" " operator F() const { }\n" "};"; const char expected[] = "class Fred { " "" "operatorint** ( ) const { } " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } // inline local variable { const char code[] = "class Fred {\n" " typedef int INT;\n" " void f1() const { INT i; }\n" "};"; const char expected[] = "class Fred { " "" "void f1 ( ) const { int i ; } " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } // out of line member variable { const char code[] = "class Fred {\n" " typedef int INT;\n" " void f1() const;\n" "};\n" "void Fred::f1() const { INT i; f(i); }"; const char expected[] = "class Fred { " "" "void f1 ( ) const ; " "} ; " "void Fred :: f1 ( ) const { int i ; f ( i ) ; }"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } // out of line operator { const char code[] = "class Fred {\n" " typedef int* (Fred::*F);\n" " operator F() const;\n" "};\n" "Fred::operator F() const { }"; const char expected[] = "class Fred { " "" "operatorint** ( ) const ; " "} ; " "Fred :: operatorint** ( ) const { }"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef73() { // ticket #2412 const char code[] = "struct B {};\n" "typedef struct A : public B {\n" " void f();\n" "} a, *aPtr;"; const char expected[] = "struct B { } ; " "struct A : public B { " "void f ( ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef74() { // ticket #2414 const char code[] = "typedef long (*state_func_t)(void);\n" "typedef state_func_t (*state_t)(void);\n" "state_t current_state = death;\n" "static char get_runlevel(const state_t);"; const char expected[] = "long ( * ( * current_state ) ( void ) ) ( void ) ; current_state = death ; " "static char get_runlevel ( const long ( * ( * ) ( void ) ) ( void ) ) ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef75() { // ticket #2426 const char code[] = "typedef _Packed struct S { long l; };"; ASSERT_EQUALS("", tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef76() { // ticket #2453 segmentation fault const char code[] = "void f1(typedef int x) {}"; const char expected[] = "void f1 ( typedef int x ) { }"; ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef77() { // ticket #2554 const char code[] = "typedef char Str[10]; int x = sizeof(Str);"; const char expected[] = "int x ; x = 10 ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef78() { // ticket #2568 const char code[] = "typedef struct A A_t;\n" "A_t a;\n" "typedef struct A { } A_t;\n" "A_t a1;"; const char expected[] = "struct A a ; struct A { } ; struct A a1 ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef79() { // ticket #2348 const char code[] = "typedef int (Tcl_ObjCmdProc) (int x);\n" "typedef struct LangVtab\n" "{\n" " Tcl_ObjCmdProc * (*V_LangOptionCommand);\n" "} LangVtab;"; const char expected[] = "struct LangVtab " "{ " "int ( * ( * V_LangOptionCommand ) ) ( int x ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef80() { // ticket #2587 const char code[] = "typedef struct s { };\n" "void f() {\n" " sizeof(struct s);\n" "};"; const char expected[] = "struct s { } ; " "void f ( ) { " "sizeof ( struct s ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef81() { // ticket #2603 segmentation fault ASSERT_THROW(checkSimplifyTypedef("typedef\n"), InternalError); checkSimplifyTypedef("typedef constexpr\n"); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef82() { // ticket #2403 checkSimplifyTypedef("class A {\n" "public:\n" " typedef int F(int idx);\n" "};\n" "class B {\n" "public:\n" " A::F ** f;\n" "};\n" "int main()\n" "{\n" " B * b = new B;\n" " b->f = new A::F * [ 10 ];\n" "}"); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef83() { // ticket #2620 const char code[] = "typedef char Str[10];\n" "void f(Str &cl) { }"; // The expected result.. const char expected[] = "void f ( char ( & cl ) [ 10 ] ) { }"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef84() { // ticket #2630 (segmentation fault) const char code1[] = "typedef y x () x"; ASSERT_THROW(checkSimplifyTypedef(code1), InternalError); const char code2[] = "typedef struct template <>"; ASSERT_THROW(checkSimplifyTypedef(code2), InternalError); const char code3[] = "typedef ::<>"; ASSERT_THROW(checkSimplifyTypedef(code3), InternalError); } void simplifyTypedef85() { // ticket #2651 const char code[] = "typedef FOO ((BAR)(void, int, const int, int*));"; const char expected[] = ";"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef86() { // ticket #2581 const char code[] = "class relational {\n" " typedef void (safe_bool_helper::*safe_bool)();\n" "public:\n" " operator safe_bool() const;\n" " safe_bool operator!() const;\n" "};"; const char expected[] = "class relational { " "" "public: " "operatorsafe_bool ( ) const ; " "safe_bool operator! ( ) const ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef87() { // ticket #2651 const char code[] = "typedef FOO (*(*BAR)(void, int, const int, int*));"; const char expected[] = ";"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef88() { // ticket #2675 const char code[] = "typedef short int (*x)(...);"; const char expected[] = ";"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef89() { // ticket #2717 const char code[] = "class Fred {\n" " typedef void f(int) const;\n" " f func;\n" "};"; const char expected[] = "class Fred { void func ( int ) const ; } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef90() { // ticket #2718 const char code[] = "typedef int IA[2];\n" "void f(const IA&) {};"; const char expected[] = "void f ( const int ( & ) [ 2 ] ) { } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef91() { // ticket #2716 const char code1[] = "namespace NS {\n" " typedef int (*T)();\n" " class A {\n" " T f();\n" " };\n" "}\n" "namespace NS {\n" " T A::f() {}\n" "}"; const char expected1[] = "namespace NS { " "" "class A { " "int * f ( ) ; " "} ; " "} " "namespace NS { " "int * A :: f ( ) { } " "}"; ASSERT_EQUALS(expected1, tok(code1)); ASSERT_EQUALS("", errout.str()); const char code2[] = "namespace NS {\n" " typedef int (*T)();\n" " class A {\n" " T f();\n" " };\n" "}\n" "NS::T NS::A::f() {}"; const char expected2[] = "namespace NS { " "" "class A { " "int * f ( ) ; " "} ; " "} " "int * NS :: A :: f ( ) { }"; ASSERT_EQUALS(expected2, tok(code2)); ASSERT_EQUALS("", errout.str()); const char code3[] = "namespace NS1 {\n" " namespace NS2 {\n" " typedef int (*T)();\n" " class A {\n" " T f();\n" " };\n" " }\n" "}\n" "namespace NS1 {\n" " namespace NS2 {\n" " T A::f() {}\n" " }\n" "}"; const char expected3[] = "namespace NS1 { " "namespace NS2 { " "" "class A { " "int * f ( ) ; " "} ; " "} " "} " "namespace NS1 { " "namespace NS2 { " "int * A :: f ( ) { } " "} " "}"; ASSERT_EQUALS(expected3, tok(code3)); ASSERT_EQUALS("", errout.str()); const char code4[] = "namespace NS1 {\n" " namespace NS2 {\n" " typedef int (*T)();\n" " class A {\n" " T f();\n" " };\n" " }\n" "}\n" "namespace NS1 {\n" " NS2::T NS2::A::f() {}\n" "}"; const char expected4[] = "namespace NS1 { " "namespace NS2 { " "" "class A { " "int * f ( ) ; " "} ; " "} " "} " "namespace NS1 { " "int * NS2 :: A :: f ( ) { } " "}"; ASSERT_EQUALS(expected4, tok(code4)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef92() { // ticket #2736 (segmentation fault) const char code[] = "typedef long Long;\n" "namespace NS {\n" "}"; ASSERT_EQUALS("", tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef93() { // ticket #2738 (syntax error) const char code[] = "struct s { double x; };\n" "typedef struct s (*binop) (struct s, struct s);"; const char expected[] = "struct s { double x ; } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef94() { // ticket #1982 const char code1[] = "class A {\n" "public:\n" " typedef struct {\n" " int a[4];\n" " } data;\n" "};\n" "A::data d;"; const char expected1[] = "class A { " "public: " "struct data { " "int a [ 4 ] ; " "} ; " "} ; " "struct A :: data d ;"; ASSERT_EQUALS(expected1, tok(code1)); ASSERT_EQUALS("", errout.str()); const char code2[] = "class A {\n" "public:\n" " typedef struct {\n" " int a[4];\n" " } data;\n" "};\n" "::A::data d;"; const char expected2[] = "class A { " "public: " "struct data { " "int a [ 4 ] ; " "} ; " "} ; " "struct :: A :: data d ;"; ASSERT_EQUALS(expected2, tok(code2)); ASSERT_EQUALS("", errout.str()); const char code3[] = "class A {\n" "public:\n" " typedef struct {\n" " int a[4];\n" " } data;\n" "};\n" "class B : public ::A::data { };"; const char expected3[] = "class A { " "public: " "struct data { " "int a [ 4 ] ; " "} ; " "} ; " "class B : public :: A :: data { } ;"; ASSERT_EQUALS(expected3, tok(code3)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef95() { // ticket #2844 const char code[] = "class symbol_table {\n" "public:\n" " typedef expression_error::error_code (*valid_func)(void *cbparam, const char *name, expression_space space);\n" " valid_func f;\n" "};"; const char expected[] = "class symbol_table { " "public: " "expression_error :: error_code * f ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef96() { // ticket #2886 (segmentation fault) const char code[] = "typedef struct x { }"; ASSERT_THROW(tok(code), InternalError); } void simplifyTypedef97() { // ticket #2983 (segmentation fault) const char code[] = "typedef x y\n" "(A); y"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef99() { // ticket #2999 const char code[] = "typedef struct Fred Fred;\n" "struct Fred { };"; tok(code); ASSERT_EQUALS("", errout.str()); const char code1[] = "struct Fred { };\n" "typedef struct Fred Fred;"; tok(code1); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef100() { // ticket #3000 const char code[] = "typedef struct Fred { } Fred;\n" "Fred * foo() {\n" " Fred *fred;\n" " fred = se_alloc(sizeof(struct Fred));\n" " return fred;\n" "}"; tok(code); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n", errout.str()); } void simplifyTypedef101() { // ticket #3003 (segmentation fault) const char code[] = "typedef a x[];\n" "y = x"; ASSERT_THROW(tok(code), InternalError); } void simplifyTypedef102() { // ticket #3004 const char code[] = "typedef struct { } Fred;\n" "void foo()\n" "{\n" " Fred * Fred;\n" "}"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef103() { // ticket #3007 const char code[] = "typedef struct { } Fred;\n" "void foo()\n" "{\n" " Fred Fred;\n" "}"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef104() { // ticket #3070 const char code[] = "typedef int (*in_func) (void FAR *, unsigned char FAR * FAR *);"; ASSERT_EQUALS(";", tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef105() { // ticket #3616 (segmentation fault) const char code[] = "( int typedef char x; ){}"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef106() { // ticket #3619 (segmentation fault) const char code[] = "typedef void f ();\ntypedef { f }"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef107() { // ticket #3963 (bad code => segmentation fault) const char code[] = "typedef int x[]; int main() { return x }"; ASSERT_THROW(tok(code), InternalError); } void simplifyTypedef108() { // ticket #4777 const char code[] = "typedef long* GEN;\n" "void sort_factor(GEN *y, long n) {\n" " GEN a, b;\n" " foo(a, b);\n" "}"; const char expected[] = "void sort_factor ( long * * y , long n ) { " "long * a ; long * b ; " "foo ( a , b ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef109() { const char code[] = "typedef int&& rref;\n" "rref var;"; const char expected[] = "int & & var ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef110() { const char code[] = "namespace A {\n" " namespace B {\n" " namespace D {\n" " typedef int DKIPtr;\n" " }\n" " struct ZClass {\n" " void set1(const A::B::D::DKIPtr& p) {\n" " membervariable1 = p;\n" " }\n" " void set2(const ::A::B::D::DKIPtr& p) {\n" " membervariable2 = p;\n" " }\n" " void set3(const B::D::DKIPtr& p) {\n" " membervariable3 = p;\n" " }\n" " void set4(const ::B::D::DKIPtr& p) {\n" " membervariable4 = p;\n" " }\n" " void set5(const C::D::DKIPtr& p) {\n" " membervariable5 = p;\n" " }\n" " A::B::D::DKIPtr membervariable1;\n" " ::A::B::D::DKIPtr membervariable2;\n" " B::D::DKIPtr membervariable3;\n" " ::B::D::DKIPtr membervariable4;\n" " C::D::DKIPtr membervariable5;\n" " };\n" " }\n" "}"; const char expected[] = "namespace A { " "namespace B { " "struct ZClass { " "void set1 ( const int & p ) { " "membervariable1 = p ; " "} " "void set2 ( const int & p ) { " "membervariable2 = p ; " "} " "void set3 ( const int & p ) { " "membervariable3 = p ; " "} " "void set4 ( const :: B :: D :: DKIPtr & p ) { " "membervariable4 = p ; " "} " "void set5 ( const C :: D :: DKIPtr & p ) { " "membervariable5 = p ; " "} " "int membervariable1 ; " "int membervariable2 ; " "int membervariable3 ; " ":: B :: D :: DKIPtr membervariable4 ; " "C :: D :: DKIPtr membervariable5 ; " "} ; " "} " "}"; ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef111() { // ticket #6345 const char code1[] = "typedef typename A B;\n" "typedef typename B C;\n" "typename C c;\n"; const char expected1[] = "typename A c ;"; ASSERT_EQUALS(expected1, tok(code1)); const char code2[] = "typedef typename A B;\n" "typedef typename B C;\n" "C c;\n"; const char expected2[] = "typename A c ;"; ASSERT_EQUALS(expected2, tok(code2)); const char code3[] = "typedef typename A B;\n" "typedef B C;\n" "C c;\n"; const char expected3[] = "typename A c ;"; ASSERT_EQUALS(expected3, tok(code3)); const char code4[] = "typedef A B;\n" "typedef typename B C;\n" "C c;\n"; const char expected4[] = "typename A c ;"; ASSERT_EQUALS(expected4, tok(code4)); const char code5[] = "typedef A B;\n" "typedef B C;\n" "C c;\n"; const char expected5[] = "A c ;"; ASSERT_EQUALS(expected5, tok(code5)); // #5614 const char code5614[] = "typedef typename T::U V;\n" "typedef typename T::W (V::*Fn)();\n"; const char expected5614[] = ";"; ASSERT_EQUALS(expected5614, tok(code5614)); } void simplifyTypedef112() { // ticket #6048 const char code[] = "template<\n" "typename DataType,\n" "typename SpaceType,\n" "typename TrafoConfig>\n" "class AsmTraits1 {\n" " typedef typename SpaceType::TrafoType TrafoType;\n" " typedef typename TrafoType::ShapeType ShapeType;\n" " typedef typename TrafoType::template Evaluator::Type TrafoEvaluator;\n" " enum {\n" " domain_dim = TrafoEvaluator::domain_dim,\n" " };\n" "};"; const char expected[] = "template < " "typename DataType , " "typename SpaceType , " "typename TrafoConfig > " "class AsmTraits1 { " "enum Anonymous0 { " "domain_dim = SpaceType :: TrafoType :: template Evaluator < SpaceType :: TrafoType :: ShapeType , DataType > :: Type :: domain_dim , " "} ; } ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef113() { // ticket #7030 const char code[] = "typedef int T;\n" "void f() { T:; }"; const char expected[] = "void f ( ) { T : ; }"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef114() { // ticket #7058 const char code[] = "typedef struct { enum {A,B}; } AB;\n" "x=AB::B;"; const char expected[] = "struct AB { enum Anonymous0 { A , B } ; } ; x = AB :: B ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef115() { // ticket #6998 const char code[] = "typedef unsigned unsignedTypedef;\n" "unsignedTypedef t1 ;\n" "unsigned t2 ;"; const char expected[] = "unsigned int t1 ; " "unsigned int t2 ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef116() { // #5624 const char code[] = "void fn() {\n" " typedef std::vector CharacterToConversion;\n" " CharacterToConversion c2c;\n" " for (CharacterToConversion::iterator it = c2c.begin(); it != c2c.end(); ++it) {}\n" " CharacterToConversion().swap(c2c);\n" "}"; const char expected[] = "void fn ( ) { " "std :: vector < CharacterConversion > c2c ; " "for ( std :: vector < CharacterConversion > :: iterator it = c2c . begin ( ) ; it != c2c . end ( ) ; ++ it ) { } " "std :: vector < CharacterConversion > ( ) . swap ( c2c ) ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef117() { // #6507 const char code[] = "typedef struct bstr {} bstr;\n" "struct bstr bstr0(const char *s) {\n" " return (struct bstr) { (unsigned char *)s, s ? strlen(s) : 0 };\n" "}"; const char expected[] = "struct bstr { } ; " "struct bstr bstr0 ( const char * s ) { " "return ( struct bstr ) { ( unsigned char * ) s , s ? strlen ( s ) : 0 } ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef118() { // #5749 const char code[] = "struct ClassyClass {\n" "int id;\n" "typedef int (ClassyClass::*funky_type);\n" "operator funky_type() {\n" "return &ClassyClass::id;\n" "}}"; const char expected[] = "struct ClassyClass { " "int id ; " "operatorintClassyClass::* ( ) { " "return & ClassyClass :: id ; " "} }"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef119() { // #7541 const char code[] = "namespace Baz {\n" " typedef char* T1;\n" " typedef T1 XX;\n" "}\n" "namespace Baz { }\n" "enum Bar { XX = 1 };"; const char exp [] = "enum Bar { XX = 1 } ;"; ASSERT_EQUALS(exp, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedefFunction1() { { const char code[] = "typedef void (*my_func)();\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (*my_func)(void);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( void ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (*my_func)(int);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (*my_func)(int*);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { // ticket # 1615 const char code[] = "typedef void (*my_func)(arg_class*);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( arg_class * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)();\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(void);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( void ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(int);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(int*);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(arg_class*);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( arg_class * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func();\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(void);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( void ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(int);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(int*);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(arg_class*);\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( arg_class * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func());\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(void));\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( void ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(int));\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(int*));\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( int * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(arg_class*));\n" "std::queue func_queue;"; // The expected result.. const char expected[] = "std :: queue < void ( * ) ( arg_class * ) > func_queue ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedefFunction2() { // ticket #1685 const char code[] = "typedef void voidfn (int);\n" "voidfn xxx;"; // The expected result.. const char expected[] = "void xxx ( int ) ;"; ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedefFunction3() { { const char code[] = "typedef C func1();\n" "typedef C (* func2)();\n" "typedef C (& func3)();\n" "typedef C (C::* func4)();\n" "typedef C (C::* func5)() const;\n" "typedef C (C::* func6)() volatile;\n" "typedef C (C::* func7)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;\n" "func5 f5;\n" "func6 f6;\n" "func7 f7;"; // The expected result.. const char expected[] = "C f1 ( ) ; " "C * f2 ; " // this gets simplified to a regular pointer "C ( & f3 ) ( ) ; " "C * f4 ; " // this gets simplified to a regular pointer "C * f5 ; " // this gets simplified to a regular pointer "C * f6 ; " // this gets simplified to a regular pointer "C * f7 ;"; // this gets simplified to a regular pointer ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef C const func1();\n" "typedef C const (* func2)();\n" "typedef C const (& func3)();\n" "typedef C const (C::* func4)();\n" "typedef C const (C::* func5)() const;\n" "typedef C const (C::* func6)() volatile;\n" "typedef C const (C::* func7)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;\n" "func5 f5;\n" "func6 f6;\n" "func7 f7;"; // The expected result.. // C const -> const C const char expected[] = "const C f1 ( ) ; " "const C * f2 ; " // this gets simplified to a regular pointer "const C ( & f3 ) ( ) ; " "const C * f4 ; " // this gets simplified to a regular pointer "const C * f5 ; " // this gets simplified to a regular pointer "const C * f6 ; " // this gets simplified to a regular pointer "const C * f7 ;"; // this gets simplified to a regular pointer ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef const C func1();\n" "typedef const C (* func2)();\n" "typedef const C (& func3)();\n" "typedef const C (C::* func4)();\n" "typedef const C (C::* func5)() const;\n" "typedef const C (C::* func6)() volatile;\n" "typedef const C (C::* func7)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;\n" "func5 f5;\n" "func6 f6;\n" "func7 f7;"; // The expected result.. const char expected[] = "const C f1 ( ) ; " "const C * f2 ; " // this gets simplified to a regular pointer "const C ( & f3 ) ( ) ; " "const C * f4 ; " // this gets simplified to a regular pointer "const C * f5 ; " // this gets simplified to a regular pointer "const C * f6 ; " // this gets simplified to a regular pointer "const C * f7 ;"; // this gets simplified to a regular pointer ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef C * func1();\n" "typedef C * (* func2)();\n" "typedef C * (& func3)();\n" "typedef C * (C::* func4)();\n" "typedef C * (C::* func5)() const;\n" "typedef C * (C::* func6)() volatile;\n" "typedef C * (C::* func7)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;\n" "func5 f5;\n" "func6 f6;\n" "func7 f7;"; // The expected result.. const char expected[] = "C * f1 ( ) ; " "C * * f2 ; " // this gets simplified to a regular pointer "C * ( & f3 ) ( ) ; " "C * * f4 ; " // this gets simplified to a regular pointer "C * * f5 ; " // this gets simplified to a regular pointer "C * * f6 ; " // this gets simplified to a regular pointer "C * * f7 ;"; // this gets simplified to a regular pointer ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef const C * func1();\n" "typedef const C * (* func2)();\n" "typedef const C * (& func3)();\n" "typedef const C * (C::* func4)();\n" "typedef const C * (C::* func5)() const;\n" "typedef const C * (C::* func6)() volatile;\n" "typedef const C * (C::* func7)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;\n" "func5 f5;\n" "func6 f6;\n" "func7 f7;"; // The expected result.. const char expected[] = "const C * f1 ( ) ; " "const C * * f2 ; " // this gets simplified to a regular pointer "const C * ( & f3 ) ( ) ; " "const C * * f4 ; " // this gets simplified to a regular pointer "const C * * f5 ; " // this gets simplified to a regular pointer "const C * * f6 ; " // this gets simplified to a regular pointer "const C * * f7 ;"; // this gets simplified to a regular pointer ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef C const * func1();\n" "typedef C const * (* func2)();\n" "typedef C const * (& func3)();\n" "typedef C const * (C::* func4)();\n" "typedef C const * (C::* func5)() const;\n" "typedef C const * (C::* func6)() volatile;\n" "typedef C const * (C::* func7)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;\n" "func5 f5;\n" "func6 f6;\n" "func7 f7;"; // The expected result.. // C const -> const C const char expected[] = "const C * f1 ( ) ; " "const C * * f2 ; " // this gets simplified to a regular pointer "const C * ( & f3 ) ( ) ; " "const C * * f4 ; " // this gets simplified to a regular pointer "const C * * f5 ; " // this gets simplified to a regular pointer "const C * * f6 ; " // this gets simplified to a regular pointer "const C * * f7 ;"; // this gets simplified to a regular pointer ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedefFunction4() { const char code[] = "typedef int ( * ( * type1 ) ( bool ) ) ( int , int ) ;\n" "typedef int ( * ( type2 ) ( bool ) ) ( int , int ) ;\n" "typedef int ( * type3 ( bool ) ) ( int , int ) ;\n" "type1 t1;\n" "type2 t2;\n" "type3 t3;"; // The expected result.. const char expected[] = "int ( * ( * t1 ) ( bool ) ) ( int , int ) ; " "int * t2 ( bool ) ; " "int * t3 ( bool ) ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedefFunction5() { const char code[] = "typedef int ( * type1 ) ( float ) ;\n" "typedef int ( * const type2 ) ( float ) ;\n" "typedef int ( * volatile type3 ) ( float ) ;\n" "typedef int ( * const volatile type4 ) ( float ) ;\n" "typedef int ( C :: * type5 ) ( float ) ;\n" "typedef int ( C :: * const type6 ) ( float ) ;\n" "typedef int ( C :: * volatile type7 ) ( float ) ;\n" "typedef int ( C :: * const volatile type8 ) ( float ) ;\n" "typedef int ( :: C :: * type9 ) ( float ) ;\n" "typedef int ( :: C :: * const type10 ) ( float ) ;\n" "typedef int ( :: C :: * volatile type11 ) ( float ) ;\n" "typedef int ( :: C :: * const volatile type12 ) ( float ) ;\n" "type1 t1;\n" "type2 t2;\n" "type3 t3;\n" "type4 t4;\n" "type5 t5;\n" "type6 t6;\n" "type7 t7;\n" "type8 t8;\n" "type9 t9;\n" "type10 t10;\n" "type11 t11;\n" "type12 t12;"; // The expected result.. const char expected[] = "int * t1 ; " // simplified to regular pointer "int * const t2 ; " "int * t3 ; " // volatile removed, gets simplified to regular pointer "int * const t4 ; " // volatile removed "int * t5 ; " "int * const t6 ; " "int * t7 ; " // volatile removed "int * const t8 ; " // volatile removed "int ( :: C :: * t9 ) ( float ) ; " "int ( :: C :: * const t10 ) ( float ) ; " "int ( :: C :: * t11 ) ( float ) ; " // volatile removed "int ( :: C :: * const t12 ) ( float ) ;"; // volatile removed ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedefFunction6() { const char code[] = "typedef void (*testfp)();\n" "struct Fred\n" "{\n" " testfp get1() { return 0; }\n" " void ( * get2 ( ) ) ( ) { return 0 ; }\n" " testfp get3();\n" " void ( * get4 ( ) ) ( );\n" "};\n" "testfp Fred::get3() { return 0; }\n" "void ( * Fred::get4 ( ) ) ( ) { return 0 ; }"; // The expected result.. const char expected[] = "struct Fred " "{ " "void * get1 ( ) { return 0 ; } " "void * get2 ( ) { return 0 ; } " "void * get3 ( ) ; " "void * get4 ( ) ; " "} ; " "void * Fred :: get3 ( ) { return 0 ; } " "void * Fred :: get4 ( ) { return 0 ; }"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedefFunction7() { const char code[] = "typedef void ( __gnu_cxx :: _SGIAssignableConcept < _Tp > :: * _func_Tp_SGIAssignableConcept ) () ;" "_func_Tp_SGIAssignableConcept X;"; // The expected result.. const char expected[] = "void ( __gnu_cxx :: _SGIAssignableConcept < _Tp > :: * X ) ( ) ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedefFunction8() { // #2376 - internal error const char code[] = "typedef int f_expand(const nrv_byte *);\n" "void f(f_expand *(*get_fexp(int))){}"; checkSimplifyTypedef(code); TODO_ASSERT_EQUALS("", "[test.cpp:2]: (debug) Function::addArguments found argument 'int' with varid 0.\n", errout.str()); // make sure that there is no internal error } void simplifyTypedefFunction9() { { const char code[] = "typedef ::C (::C::* func1)();\n" "typedef ::C (::C::* func2)() const;\n" "typedef ::C (::C::* func3)() volatile;\n" "typedef ::C (::C::* func4)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;"; // The expected result.. const char expected[] = ":: C ( :: C :: * f1 ) ( ) ; " ":: C ( :: C :: * f2 ) ( ) const ; " ":: C ( :: C :: * f3 ) ( ) ; " ":: C ( :: C :: * f4 ) ( ) const ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef B::C (B::C::* func1)();\n" "typedef B::C (B::C::* func2)() const;\n" "typedef B::C (B::C::* func3)() volatile;\n" "typedef B::C (B::C::* func4)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;"; // The expected result.. const char expected[] = "B :: C * f1 ; " "B :: C * f2 ; " "B :: C * f3 ; " "B :: C * f4 ;"; ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef ::B::C (::B::C::* func1)();\n" "typedef ::B::C (::B::C::* func2)() const;\n" "typedef ::B::C (::B::C::* func3)() volatile;\n" "typedef ::B::C (::B::C::* func4)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;"; // The expected result.. const char expected[] = ":: B :: C ( :: B :: C :: * f1 ) ( ) ; " ":: B :: C ( :: B :: C :: * f2 ) ( ) const ; " ":: B :: C ( :: B :: C :: * f3 ) ( ) ; " ":: B :: C ( :: B :: C :: * f4 ) ( ) const ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef A::B::C (A::B::C::* func1)();\n" "typedef A::B::C (A::B::C::* func2)() const;\n" "typedef A::B::C (A::B::C::* func3)() volatile;\n" "typedef A::B::C (A::B::C::* func4)() const volatile;\n" "func1 f1;\n" "func2 f2;\n" "func3 f3;\n" "func4 f4;"; // The expected result.. const char expected[] = "A :: B :: C * f1 ; " "A :: B :: C * f2 ; " "A :: B :: C * f3 ; " "A :: B :: C * f4 ;"; ASSERT_EQUALS(expected, tok(code, true, Settings::Native, false)); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedefFunction10() { const char code[] = "enum Format_E1 { FORMAT11, FORMAT12 } Format_T1;\n" "namespace MySpace {\n" " enum Format_E2 { FORMAT21, FORMAT22 } Format_T2;\n" "}\n" "typedef Format_E1 (**PtrToFunPtr_Type1)();\n" "typedef MySpace::Format_E2 (**PtrToFunPtr_Type2)();\n" "PtrToFunPtr_Type1 t1;\n" "PtrToFunPtr_Type2 t2;"; ASSERT_EQUALS("enum Format_E1 { FORMAT11 , FORMAT12 } ; enum Format_E1 Format_T1 ; " "namespace MySpace { " "enum Format_E2 { FORMAT21 , FORMAT22 } ; enum Format_E2 Format_T2 ; " "} " "Format_E1 * * t1 ; " "MySpace :: Format_E2 * * t2 ;", tok(code,false)); } void simplifyTypedefShadow() { // shadow variable (#4445) const char code[] = "typedef struct { int x; } xyz;;\n" "void f(){\n" " int abc, xyz;\n" // <- shadow variable "}"; ASSERT_EQUALS("struct xyz { int x ; } ; void f ( ) { int abc ; int xyz ; }", tok(code,false)); } }; REGISTER_TEST(TestSimplifyTypedef) cppcheck-1.82/test/testsizeof.cpp000066400000000000000000000740331322667425100171250ustar00rootroot00000000000000/* * 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 "checksizeof.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include class TestSizeof : public TestFixture { public: TestSizeof() : TestFixture("TestSizeof") { } private: Settings settings; void run() { settings.addEnabled("warning"); settings.addEnabled("portability"); settings.inconclusive = true; TEST_CASE(sizeofsizeof); TEST_CASE(sizeofCalculation); TEST_CASE(checkPointerSizeof); TEST_CASE(checkPointerSizeofStruct); TEST_CASE(sizeofDivisionMemset); TEST_CASE(sizeofForArrayParameter); TEST_CASE(sizeofForNumericParameter); TEST_CASE(suspiciousSizeofCalculation); TEST_CASE(sizeofVoid); TEST_CASE(customStrncat); } 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... CheckSizeof checkSizeof(&tokenizer, &settings, this); checkSizeof.runChecks(&tokenizer, &settings, this); } void checkP(const char code[]) { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector files; files.push_back("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... CheckSizeof checkSizeof(&tokenizer, &settings, this); checkSizeof.runChecks(&tokenizer, &settings, this); } void sizeofsizeof() { check("void foo()\n" "{\n" " int i = sizeof sizeof char;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str()); check("void foo()\n" "{\n" " int i = sizeof (sizeof long);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str()); check("void foo(long *p)\n" "{\n" " int i = sizeof (sizeof (p));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str()); } void sizeofCalculation() { check("int a, b; int a,sizeof(a+b)"); ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str()); check("int a, b; sizeof(a*b)"); ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str()); check("int a, b; sizeof(-a)"); ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str()); check("int a, b; sizeof(*a)"); ASSERT_EQUALS("", errout.str()); check("sizeof(void * const)"); ASSERT_EQUALS("", errout.str()); check("sizeof(int*[2])"); ASSERT_EQUALS("", errout.str()); check("sizeof(Fred**)"); ASSERT_EQUALS("", errout.str()); check("sizeof(foo++)"); ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str()); check("sizeof(--foo)"); ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str()); // #6888 checkP("#define SIZEOF1 sizeof(i != 2)\n" "#define SIZEOF2 ((sizeof(i != 2)))\n" "#define VOIDCAST1 (void)\n" "#define VOIDCAST2(SZ) static_cast(SZ)\n" "int f(int i) {\n" " VOIDCAST1 SIZEOF1;\n" " VOIDCAST1 SIZEOF2;\n" " VOIDCAST2(SIZEOF1);\n" " VOIDCAST2(SIZEOF2);\n" " return i + foo(1);\n" "}"); ASSERT_EQUALS("", errout.str()); checkP("#define SIZEOF1 sizeof(i != 2)\n" "#define SIZEOF2 ((sizeof(i != 2)))\n" "int f(int i) {\n" " SIZEOF1;\n" " SIZEOF2;\n" " return i + foo(1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Found calculation inside sizeof().\n" "[test.cpp:5]: (warning, inconclusive) Found calculation inside sizeof().\n", errout.str()); } void sizeofForArrayParameter() { check("void f() {\n" " int a[10];\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " unsigned int a = 2;\n" " unsigned int b = 2;\n" " int c[(a+b)];\n" " std::cout << sizeof(c) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " unsigned int a = { 2 };\n" " unsigned int b[] = { 0 };\n" " int c[a[b[0]]];\n" " std::cout << sizeof(c) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " unsigned int a[] = { 1 };\n" " unsigned int b = 2;\n" " int c[(a[0]+b)];\n" " std::cout << sizeof(c) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[] = { 1, 2, 3 };\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[3] = { 1, 2, 3 };\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f( int a[]) {\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Using 'sizeof' on array given as " "function argument returns size of a pointer.\n", errout.str()); check("void f( int a[]) {\n" " std::cout << sizeof a / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Using 'sizeof' on array given as " "function argument returns size of a pointer.\n", errout.str()); check("void f( int a[3] ) {\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Using 'sizeof' on array given as " "function argument returns size of a pointer.\n", errout.str()); check("typedef char Fixname[1000];\n" "int f2(Fixname& f2v) {\n" " int i = sizeof(f2v);\n" " printf(\"sizeof f2v %d\", i);\n" " }\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " p[0] = 0;\n" " int unused = sizeof(p);\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char p[] = \"test\";\n" " int unused = sizeof(p);\n" "}\n" ); ASSERT_EQUALS("", errout.str()); // ticket #2495 check("void f() {\n" " static float col[][3]={\n" " {1,0,0},\n" " {0,0,1},\n" " {0,1,0},\n" " {1,0,1},\n" " {1,0,1},\n" " {1,0,1},\n" " };\n" " const int COL_MAX=sizeof(col)/sizeof(col[0]);\n" "}\n" ); ASSERT_EQUALS("", errout.str()); // ticket #155 check("void f() {\n" " char buff1[1024*64],buff2[sizeof(buff1)*2];\n" "}\n" ); ASSERT_EQUALS("", errout.str()); // ticket #2510 check("void f( int a[], int b) {\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Using 'sizeof' on array given as " "function argument returns size of a pointer.\n", errout.str()); // ticket #2510 check("void f( int a[3] , int b[2] ) {\n" " std::cout << sizeof(a) / sizeof(int) << std::endl;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Using 'sizeof' on array given as " "function argument returns size of a pointer.\n", errout.str()); // ticket #2510 check("void f() {\n" " char buff1[1024*64],buff2[sizeof(buff1)*(2+1)];\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void sizeofForNumericParameter() { check("void f() {\n" " std::cout << sizeof(10) << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str()); check("void f() {\n" " std::cout << sizeof(-10) << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str()); check("void f() {\n" " std::cout << sizeof 10 << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str()); check("void f() {\n" " std::cout << sizeof -10 << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str()); } void suspiciousSizeofCalculation() { check("int* p;\n" "return sizeof(p)/5;"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Division of result of sizeof() on pointer type.\n", errout.str()); check("unknown p;\n" "return sizeof(p)/5;"); ASSERT_EQUALS("", errout.str()); check("return sizeof(unknown)/5;"); ASSERT_EQUALS("", errout.str()); check("int p;\n" "return sizeof(p)/5;"); ASSERT_EQUALS("", errout.str()); check("int* p[5];\n" "return sizeof(p)/5;"); ASSERT_EQUALS("", errout.str()); check("return sizeof(foo)*sizeof(bar);"); ASSERT_EQUALS("[test.cpp:1]: (warning, inconclusive) Multiplying sizeof() with sizeof() indicates a logic error.\n", errout.str()); check("return (foo)*sizeof(bar);"); ASSERT_EQUALS("", errout.str()); check("return sizeof(foo)*bar;"); ASSERT_EQUALS("", errout.str()); check("return (end - source) / sizeof(encode_block_type) * sizeof(encode_block_type);"); ASSERT_EQUALS("", errout.str()); } void checkPointerSizeof() { check("void f() {\n" " char *x = malloc(10);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(*x));\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(x));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = (int*)malloc(sizeof(x));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = static_cast(malloc(sizeof(x)));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(&x));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int*));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " free(x);\n" " int **y = malloc(sizeof(int*));\n" " free(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(100 * sizeof(x));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(x) * 100);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof *x);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof x);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(100 * sizeof x);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = calloc(1, sizeof(*x));\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = calloc(1, sizeof *x);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = calloc(1, sizeof(x));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = calloc(1, sizeof x);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = calloc(1, sizeof(int));\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char x[10];\n" " memset(x, 0, sizeof(x));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char* x[10];\n" " memset(x, 0, sizeof(x));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char x[10];\n" " memset(x, 0, sizeof x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " memset(x, 0, sizeof(int));\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " memset(x, 0, sizeof(*x));\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " memset(x, 0, sizeof *x);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " memset(x, 0, sizeof x);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int));\n" " memset(x, 0, sizeof(x));\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int) * 10);\n" " memset(x, 0, sizeof(x) * 10);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int) * 10);\n" " memset(x, 0, sizeof x * 10);\n" " free(x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Size of pointer 'x' used instead of size of its data.\n", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int) * 10);\n" " memset(x, 0, sizeof(*x) * 10);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int) * 10);\n" " memset(x, 0, sizeof *x * 10);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *x = malloc(sizeof(int) * 10);\n" " memset(x, 0, sizeof(int) * 10);\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "int fun(const char *buf1)\n" "{\n" " const char *buf1_ex = \"foobarbaz\";\n" " return strncmp(buf1, buf1_ex, sizeof(buf1_ex)) == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Size of pointer 'buf1_ex' used instead of size of its data.\n", errout.str()); check( "int fun(const char *buf1) {\n" " return strncmp(buf1, foo(buf2), sizeof(buf1)) == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'buf1' used instead of size of its data.\n", errout.str()); check("int fun(const char *buf2) {\n" " return strncmp(buf1, buf2, sizeof(char*)) == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Size of pointer 'buf2' used instead of size of its data.\n", errout.str()); // #ticket 3874 check("void f()\n" "{\n" " int * pIntArray[10];\n" " memset(pIntArray, 0, sizeof(pIntArray));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void FreeFileName(const char *s) {\n" " CxString tbuf;\n" " const char *p;\n" " memcpy(s, siezof(s));\n" // non-standard memcpy "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " module_config_t *tab = module;\n" " memset(tab + confsize, 0, sizeof(tab[confsize]));\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(char* aug) {\n" " memmove(aug + extra_string, aug, buf - (bfd_byte *)aug);\n" // #7100 "}"); ASSERT_EQUALS("", errout.str()); // #7518 check("bool create_iso_definition(cpp_reader *pfile, cpp_macro *macro) {\n" " cpp_token *token;\n" " cpp_hashnode **params = malloc(sizeof(cpp_hashnode *) * macro->paramc);\n" " memcpy(params, macro->params, sizeof(cpp_hashnode *) * macro->paramc);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* foo() {\n" " void* AtomName = malloc(sizeof(char *) * 34);\n" " return AtomName;\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkPointerSizeofStruct() { check("void f() {\n" " struct foo *ptr;\n" " memset( ptr->bar, 0, sizeof ptr->bar );\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct foo {\n" " char bar[10];\n" " }* ptr;\n" " memset( ptr->bar, 0, sizeof ptr->bar );\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct foo {\n" " char *bar;\n" " }* ptr;\n" " memset( ptr->bar, 0, sizeof ptr->bar );\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Size of pointer 'bar' used instead of size of its data.\n", errout.str()); } void sizeofDivisionMemset() { check("void foo(memoryMapEntry_t* entry, memoryMapEntry_t* memoryMapEnd) {\n" " memmove(entry, entry + 1, (memoryMapEnd - entry) / sizeof(entry));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Division by result of sizeof(). memmove() expects a size in bytes, did you intend to multiply instead?\n", errout.str()); check("Foo* allocFoo(int num) {\n" " return malloc(num / sizeof(Foo));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Division by result of sizeof(). malloc() expects a size in bytes, did you intend to multiply instead?\n", errout.str()); check("void f() {\n" " char str[100];\n" " strncpy(str, xyz, sizeof(str)/sizeof(str[0]));\n" "}"); ASSERT_EQUALS("", errout.str()); } void sizeofVoid() { check("void f() {\n" " int size = sizeof(void);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Behaviour of 'sizeof(void)' is not covered by the ISO C standard.\n", errout.str()); check("void f() {\n" " void* p;\n" " int size = sizeof(*p);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) '*p' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard.\n", errout.str()); check("void f() {\n" " void* p = malloc(10);\n" " int* p2 = p + 4;\n" " int* p3 = p - 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) 'p' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" "[test.cpp:4]: (portability) 'p' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("void f() {\n" " void* p1 = malloc(10);\n" " void* p2 = malloc(5);\n" " p1--;\n" " p2++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) 'p1' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" "[test.cpp:5]: (portability) 'p2' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("void f() {\n" " void* p1 = malloc(10);\n" " void* p2 = malloc(5);\n" " p1-=4;\n" " p2+=4;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) 'p1' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" "[test.cpp:5]: (portability) 'p2' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("void f() {\n" " void* p = malloc(10);\n" " int* p2 = &p + 4;\n" " int* p3 = &p - 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void** p1 = malloc(10);\n" " p1--;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void** p1;\n" " int j = sizeof(*p1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void* p1[5];\n" " int j = sizeof(*p1);\n" "}"); ASSERT_EQUALS("", errout.str()); // Calculations on void* with casts check("void f(void *data) {\n" " *((unsigned char *)data + 1) = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(void *data) {\n" " *((unsigned char *)(data) + 1) = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(void *data) {\n" " unsigned char* c = (unsigned char *)(data + 1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) 'data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("void f(void *data) {\n" " unsigned char* c = (unsigned char *)data++;\n" " unsigned char* c2 = (unsigned char *)++data;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) 'data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" "[test.cpp:3]: (portability) 'data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("void f(void *data) {\n" " void* data2 = data + 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) 'data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); // #4908 (void pointer as a member of a struct/class) check("struct FOO {\n" " void *data;\n" "};\n" "char f(struct FOO foo) {\n" " char x = *((char*)(foo.data+1));\n" " foo.data++;\n" " return x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (portability) 'foo.data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" "[test.cpp:6]: (portability) 'foo.data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("struct FOO {\n" " void *data;\n" "};\n" "char f(struct FOO foo) {\n" " char x = *((char*)foo.data+1);\n" " return x;\n" "}\n" "char f2(struct FOO foo) {\n" " char x = *((char*)((FOO)foo).data + 1);\n" " return x;\n" "}\n" "char f3(struct FOO* foo) {\n" " char x = *((char*)foo->data + 1);\n" " return x;\n" "}\n" "struct BOO {\n" " FOO data;\n" "};\n" "void f4(struct BOO* boo) {\n" " char c = *((char*)boo->data.data + 1);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct FOO {\n" " void *data;\n" "};\n" "char f(struct FOO* foo) {\n" " *(foo[1].data + 1) = 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (portability) 'foo[1].data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); check("struct FOO {\n" " void *data;\n" "};\n" "void f2(struct FOO* foo) {\n" " (foo[0]).data++;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (portability) '(foo[0]).data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); // #6050 arithmetic on void** check("void* array[10];\n" "void** b = array + 3;\n"); ASSERT_EQUALS("", errout.str()); } void customStrncat() { // Ensure we don't crash on custom-defined strncat, ticket #5875 check("char strncat ();\n" "int main () {\n" " return strncat ();\n" "}\n"); } }; REGISTER_TEST(TestSizeof) cppcheck-1.82/test/teststl.cpp000066400000000000000000003607631322667425100164400ustar00rootroot00000000000000/* * 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 "checkstl.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "tokenize.h" #include #include class TestStl : public TestFixture { public: TestStl() : TestFixture("TestStl") { } private: Settings settings; void run() { settings.addEnabled("warning"); settings.addEnabled("style"); settings.addEnabled("performance"); LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(iterator1); TEST_CASE(iterator2); TEST_CASE(iterator3); TEST_CASE(iterator4); TEST_CASE(iterator5); TEST_CASE(iterator6); TEST_CASE(iterator7); TEST_CASE(iterator8); TEST_CASE(iterator9); TEST_CASE(iterator10); TEST_CASE(iterator11); TEST_CASE(iterator12); TEST_CASE(iterator13); TEST_CASE(iterator14); // #8191 TEST_CASE(dereference); TEST_CASE(dereference_break); // #3644 - handle "break" TEST_CASE(dereference_member); TEST_CASE(STLSize); TEST_CASE(STLSizeNoErr); TEST_CASE(negativeIndex); TEST_CASE(erase1); TEST_CASE(erase2); TEST_CASE(erase3); TEST_CASE(erase4); TEST_CASE(erase5); TEST_CASE(erase6); TEST_CASE(eraseBreak); TEST_CASE(eraseContinue); TEST_CASE(eraseReturn1); TEST_CASE(eraseReturn2); TEST_CASE(eraseReturn3); TEST_CASE(eraseGoto); TEST_CASE(eraseAssign1); TEST_CASE(eraseAssign2); TEST_CASE(eraseAssign3); TEST_CASE(eraseAssign4); TEST_CASE(eraseAssignByFunctionCall); TEST_CASE(eraseErase); TEST_CASE(eraseByValue); TEST_CASE(eraseIf); TEST_CASE(eraseOnVector); TEST_CASE(pushback1); TEST_CASE(pushback2); TEST_CASE(pushback3); TEST_CASE(pushback4); TEST_CASE(pushback5); TEST_CASE(pushback6); TEST_CASE(pushback7); TEST_CASE(pushback8); TEST_CASE(pushback9); TEST_CASE(pushback10); TEST_CASE(pushback11); TEST_CASE(pushback12); TEST_CASE(pushback13); TEST_CASE(insert1); TEST_CASE(insert2); TEST_CASE(stlBoundaries1); TEST_CASE(stlBoundaries2); TEST_CASE(stlBoundaries3); TEST_CASE(stlBoundaries4); // #4364 TEST_CASE(stlBoundaries5); // #4352 TEST_CASE(stlBoundaries6); // #7106 // if (str.find("ab")) TEST_CASE(if_find); TEST_CASE(if_str_find); TEST_CASE(size1); TEST_CASE(size2); TEST_CASE(size3); TEST_CASE(size4); // #2652 - don't warn about vector/deque // Redundant conditions.. // if (ints.find(123) != ints.end()) ints.remove(123); TEST_CASE(redundantCondition1); // missing inner comparison when incrementing iterator inside loop TEST_CASE(missingInnerComparison1); TEST_CASE(missingInnerComparison2); // no FP when there is comparison TEST_CASE(missingInnerComparison3); // no FP when there is iterator shadowing TEST_CASE(missingInnerComparison4); // no FP when "break;" is used TEST_CASE(missingInnerComparison5); // Ticket #2154 - FP TEST_CASE(missingInnerComparison6); // #2643 - 'it=foo.insert(++it,0);' // catch common problems when using the string::c_str() function TEST_CASE(cstr); TEST_CASE(autoPointer); TEST_CASE(uselessCalls); TEST_CASE(stabilityOfChecks); // #4684 cppcheck crash in template function call TEST_CASE(dereferenceInvalidIterator); TEST_CASE(dereferenceInvalidIterator2); // #6572 TEST_CASE(dereference_auto); TEST_CASE(readingEmptyStlContainer); } void check(const char code[], const bool inconclusive=false, const Standards::cppstd_t cppstandard=Standards::CPP11) { // Clear the error buffer.. errout.str(""); settings.inconclusive = inconclusive; settings.standards.cpp = cppstandard; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckStl checkStl(&tokenizer, &settings, this); checkStl.runSimplifiedChecks(&tokenizer, &settings, this); } void check(const std::string &code, const bool inconclusive=false) { check(code.c_str(), inconclusive); } void iterator1() { check("void f()\n" "{\n" " list l1;\n" " list l2;\n" " for (list::iterator it = l1.begin(); it != l2.end(); ++it)\n" " { }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); check("void f()\n" "{\n" " list l1;\n" " list l2;\n" " for (list::iterator it = l1.begin(); l2.end() != it; ++it)\n" " { }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); check("struct C { std::list l1; void func(); };\n" "void C::func() {\n" " std::list::iterator it;\n" " for (it = l1.begin(); it != l1.end(); ++it) { }\n" " C c;\n" " for (it = c.l1.begin(); it != c.l1.end(); ++it) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // Same check with reverse iterator check("void f()\n" "{\n" " list l1;\n" " list l2;\n" " for (list::const_reverse_iterator it = l1.rbegin(); it != l2.rend(); ++it)\n" " { }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); } void iterator2() { check("void foo()\n" "{\n" " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " while (it != l2.end())\n" " {\n" " ++it;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); } void iterator3() { check("void foo()\n" "{\n" " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " l2.insert(it, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); check("void foo() {\n" // #5803 " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " l2.insert(it, l1.end());\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // #7658 " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " list::iterator end = l1.end();\n" " l2.insert(it, end);\n" "}"); ASSERT_EQUALS("", errout.str()); // only warn for insert when there are preciself 2 arguments. check("void foo() {\n" " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " l2.insert(it);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " list l1;\n" " list l2;\n" " list::iterator it = l1.begin();\n" " l2.insert(it,0,1);\n" "}"); ASSERT_EQUALS("", errout.str()); } void iterator4() { check("void foo(std::vector &test)\n" "{\n" " std::set result;\n" " for (std::vector::const_iterator cit = test.begin();\n" " cit != test.end();\n" " ++cit)\n" " {\n" " result.insert(cit->size());\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void iterator5() { check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::find(ints1.begin(), ints2.end(), 22);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str()); } void iterator6() { // Ticket #1357 check("void foo(const std::set &ints1)\n" "{\n" " std::set ints2;\n" " std::set::iterator it1 = ints1.begin();\n" " std::set::iterator it2 = ints1.end();\n" " ints2.insert(it1, it2);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const std::set &ints1)\n" "{\n" " std::set ints2;\n" " std::set::iterator it1 = ints1.begin();\n" " std::set::iterator it2 = ints2.end();\n" " ints2.insert(it1, it2);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Iterators of different containers are used together.\n", "", errout.str()); } void iterator7() { check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::inplace_merge(ints1.begin(), std::advance(ints1.rbegin(), 5), ints2.end());\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str()); check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::inplace_merge(ints1.begin(), std::advance(ints2.rbegin(), 5), ints1.end());\n" "}"); ASSERT_EQUALS("", errout.str()); } void iterator8() { check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::find_first_of(ints1.begin(), ints2.end(), ints1.begin(), ints1.end());\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str()); check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::find_first_of(ints1.begin(), ints1.end(), ints2.begin(), ints1.end());\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str()); check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::find_first_of(foo.bar.begin(), foo.bar.end()-6, ints2.begin(), ints1.end());\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str()); check("void foo()\n" "{\n" " std::vector ints1;\n" " std::vector ints2;\n" " std::vector::iterator it = std::find_first_of(ints1.begin(), ints1.end(), ints2.begin(), ints2.end());\n" "}"); ASSERT_EQUALS("", errout.str()); // #6839 check("void f(const std::wstring& a, const std::wstring& b) {\n" " const std::string tp1 = std::string(a.begin(), b.end());\n" " const std::wstring tp2 = std::string(b.begin(), a.end());\n" " const std::u16string tp3(a.begin(), b.end());\n" " const std::u32string tp4(b.begin(), a.end());\n" " const std::string fp1 = std::string(a.begin(), a.end());\n" " const std::string tp2(a.begin(), a.end());\n" "}"); ASSERT_EQUALS(// TODO "[test.cpp:2]: (error) Iterators of different containers are used together.\n" // TODO "[test.cpp:3]: (error) Iterators of different containers are used together.\n" "[test.cpp:4]: (error) Iterators of different containers are used together.\n" "[test.cpp:5]: (error) Iterators of different containers are used together.\n", errout.str()); } void iterator9() { // Ticket #1600 check("void foo(std::vector &r)\n" "{\n" " std::vector::iterator aI = r.begin();\n" " while(aI != r.end())\n" " {\n" " if (*aI == 0)\n" " {\n" " r.insert(aI, 42);\n" " return;\n" " }\n" " ++aI;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2481 check("void foo(std::vector &r)\n" "{\n" " std::vector::iterator aI = r.begin();\n" " while(aI != r.end())\n" " {\n" " if (*aI == 0)\n" " {\n" " r.insert(aI, 42);\n" " break;\n" " }\n" " ++aI;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Execution path checking.. check("void foo(std::vector &r, int c)\n" "{\n" " std::vector::iterator aI = r.begin();\n" " while(aI != r.end())\n" " {\n" " if (*aI == 0)\n" " {\n" " r.insert(aI, 42);\n" " if (c)\n" " {\n" " return;\n" " }\n" " }\n" " ++aI;\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:14] (error) After insert(), the iterator 'aI' may be invalid.", "", errout.str()); } void iterator10() { // Ticket #1679 check("void foo()\n" "{\n" " std::set s1;\n" " std::set s2;\n" " for (std::set::iterator it = s1.begin(); it != s1.end(); ++it)\n" " {\n" " if (true) { }\n" " if (it != s2.end()) continue;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Same iterator is used with different containers 's1' and 's2'.\n", errout.str()); } void iterator11() { // Ticket #3433 check("int main() {\n" " map myMap;\n" " vector myVector;\n" " for(vector::iterator x = myVector.begin(); x != myVector.end(); x++)\n" " myMap.erase(*x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void iterator12() { // Ticket #3201 check("void f() {\n" " std::map map1;\n" " std::map map2;\n" " std::map::const_iterator it = map1.find(123);\n" " if (it == map2.end()) { }" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); check("void f() {\n" " std::map map1;\n" " std::map map2;\n" " std::map::const_iterator it = map1.find(123);\n" " if (map2.end() == it) { }" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Same iterator is used with different containers 'map1' and 'map2'.\n", errout.str()); check("void f(std::string &s) {\n" " int pos = s.find(x);\n" " s.erase(pos);\n" " s.erase(pos);\n" "}"); ASSERT_EQUALS("", errout.str()); } void iterator13() { check("void f() {\n" " std::vector a;\n" " std::vector t;\n" " std::vector::const_iterator it;\n" " it = a.begin();\n" " while (it!=a.end())\n" " ++it;\n" " it = t.begin();\n" " while (it!=a.end())\n" " ++it;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Same iterator is used with different containers 't' and 'a'.\n", errout.str()); // #4062 check("void f() {\n" " std::vector a;\n" " std::vector t;\n" " std::vector::const_iterator it;\n" " it = a.begin();\n" " while (it!=a.end())\n" " v++it;\n" " it = t.begin();\n" " while (it!=t.end())\n" " ++it;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::vector a;\n" " std::vector t;\n" " std::vector::const_iterator it;\n" " if(z)\n" " it = a.begin();\n" " else\n" " it = t.begin();\n" " while (z && it!=a.end())\n" " v++it;\n" " while (!z && it!=t.end())\n" " v++it;\n" "}"); ASSERT_EQUALS("", errout.str()); } void iterator14() { check("void f() {\n" " std::map x;\n" " std::map::const_iterator it;\n" " for (it = x.find(0)->second.begin(); it != x.find(0)->second.end(); ++it) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } // Dereferencing invalid pointer void dereference() { check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::iterator iter;\n" " iter = ints.begin() + 2;\n" " ints.erase(iter);\n" " std::cout << (*iter) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6]: (error) Iterator 'iter' used after element has been erased.\n", errout.str()); // #6554 "False positive eraseDereference - erase in while() loop" check("typedef std::map packetMap;\n" "packetMap waitingPackets;\n" "void ProcessRawPacket() {\n" " packetMap::iterator wpi;\n" " while ((wpi = waitingPackets.find(lastInOrder + 1)) != waitingPackets.end()) {\n" " waitingPackets.erase(wpi);\n" " for (unsigned pos = 0; pos < buf.size(); ) { }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void dereference_break() { // #3644 check("void f(std::vector &ints) {\n" " std::vector::iterator iter;\n" " for (iter=ints.begin();iter!=ints.end();++iter) {\n" " if (*iter == 2) {\n" " ints.erase(iter);\n" " break;\n" " }\n" " if (*iter == 3) {\n" " ints.erase(iter);\n" " break;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void dereference_member() { check("void f()\n" "{\n" " std::map ints;\n" " std::map::iterator iter;\n" " iter = ints.begin();\n" " ints.erase(iter);\n" " std::cout << iter->first << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6]: (error) Iterator 'iter' used after element has been erased.\n", errout.str()); // Reverse iterator check("void f()\n" "{\n" " std::map ints;\n" " std::map::reverse_iterator iter;\n" " iter = ints.rbegin();\n" " ints.erase(iter);\n" " std::cout << iter->first << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6]: (error) Iterator 'iter' used after element has been erased.\n", errout.str()); } void dereference_auto() { check("void f()\n" "{\n" " std::vector ints;\n" " auto iter = ints.begin() + 2;\n" " ints.erase(iter);\n" " std::cout << (*iter) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (error) Iterator 'iter' used after element has been erased.\n", errout.str()); check("void f() {\n" " auto x = *myList.begin();\n" " myList.erase(x);\n" " auto b = x.first;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {\n" " if (auto *TD = dyn_cast(this)) {\n" " auto From = TD->getInstantiatedFrom();\n" " }\n" " return nullptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } void STLSize() { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " foo[ii] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str()); check("void foo(std::vector foo) {\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii) {\n" " foo.at(ii) = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) When ii==foo.size(), foo.at(ii) is out of bounds.\n", errout.str()); check("void foo(const std::string& foo) {\n" " for (unsigned int ii = 0; ii <= foo.length(); ++ii) {\n" " foo[ii] = 'x';\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str()); check("void foo(const std::string& foo, unsigned int ii) {\n" " if (ii <= foo.length()) {\n" " foo[ii] = 'x';\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str()); check("void foo(const std::string& foo, unsigned int ii) {\n" " do {\n" " foo[ii] = 'x';\n" " ++i;\n" " } while(ii <= foo.length());\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str()); check("void foo(const std::string& foo, unsigned int ii) {\n" " if (anything()) {\n" " } else if (ii <= foo.length()) {\n" " foo[ii] = 'x';\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) When ii==foo.size(), foo[ii] is out of bounds.\n", errout.str()); check("void foo()\n" "{\n" " std::vector foo;\n" " foo.push_back(1);\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " }\n" " int ii = 0;\n" " foo[ii] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " for (B b : D()) {}\n" // Don't crash on range-based for-loop "}"); ASSERT_EQUALS("", errout.str()); } void STLSizeNoErr() { { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii < foo.size(); ++ii)\n" " {\n" " foo[ii] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } { check("void foo()\n" "{\n" " std::vector foo;\n" " for (unsigned int ii = 0; ii <= foo.size(); ++ii)\n" " {\n" " if (ii == foo.size())\n" " {\n" " }\n" " else\n" " {\n" " foo[ii] = 0;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } { check("void f(const std::map &data) {\n" " int i = x;" " for (int i = 5; i <= data.size(); i++)\n" " data[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } } void negativeIndex() { check("void f(const std::vector &v) {\n" " v[-11] = 123;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Array index -11 is out of bounds.\n", errout.str()); } void erase1() { check("void f()\n" "{\n" " std::list::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it) {\n" " foo.erase(it);\n" " }\n" " for (it = foo.begin(); it != foo.end(); ++it) {\n" " foo.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Iterator 'it' used after element has been erased.\n" "[test.cpp:7] -> [test.cpp:8]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); check("void f(std::list &ints)\n" "{\n" " std::list::iterator i = ints.begin();\n" " i = ints.erase(i);\n" " *i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::list::iterator i;\n" " while (i != x.y.end())\n" " i = x.y.erase(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // #2101 check("void f(vector< list > &ints, unsigned int i)\n" "{\n" " list::iterator it;\n" " for(it = ints[i].begin(); it != ints[i].end(); it++) {\n" " if (*it % 2)\n" " it = ints[i].erase(it);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void erase2() { check("static void f()\n" "{\n" " for (iterator it = foo.begin(); it != foo.end(); it = next)\n" " {\n" " next = it;\n" " next++;\n" " foo.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void erase3() { check("static void f(std::list &foo)\n" "{\n" " std::list::iterator it = foo.begin();\n" " foo.erase(it->a);\n" " if (it->b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void erase4() { check("void f()\n" "{\n" " std::list::iterator it, it2;\n" " for (it = foo.begin(); it != i2; ++it)\n" " {\n" " foo.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); check("void f()\n" "{\n" " std::list::iterator it = foo.begin();\n" " for (; it != i2; ++it)\n" " {\n" " foo.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); check("void f()\n" "{\n" " std::list::iterator it = foo.begin();\n" " while (it != i2)\n" " {\n" " foo.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); check("void f()\n" "{\n" " std::list::iterator it = foo.begin();\n" " while (it != i2)\n" " {\n" " foo.erase(++it);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); } void erase5() { check("void f()\n" "{\n" " std::list foo;\n" " std::list::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (*it == 123)\n" " foo.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:8]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); } void erase6() { check("void f() {\n" " std::vector vec(3);\n" " std::vector::iterator it;\n" " std::vector::iterator itEnd = vec.end();\n" " for (it = vec.begin(); it != itEnd; it = vec.begin(), itEnd = vec.end())\n" " {\n" " vec.erase(it);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseBreak() { check("void f()\n" "{\n" " for (std::vector::iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " if (x)" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); check("void f()\n" "{\n" " for (std::vector::iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (x) {\n" " foo.erase(it);\n" " break;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x)\n" "{\n" " for (std::vector::iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " if (x)" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); } void eraseContinue() { check("void f(std::vector &ints)\n" "{\n" " std::vector::iterator it;\n" " std::vector::iterator jt = ints.begin();\n" " for (it = ints.begin(); it != ints.end(); it = jt) {\n" " ++jt;\n" " if (*it == 1) {\n" " jt = ints.erase(it);\n" " continue;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::map my_map) {\n" // #7365 " std::map::iterator itr = my_map.begin();\n" " switch (itr->first) {\n" " case 0:\n" " my_map.erase(itr);\n" " continue;\n" " case 1:\n" " itr->second = 1;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseReturn1() { check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseReturn2() { check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (*it == 1) {\n" " foo.erase(it);\n" " return;\n" " }\n" " else {\n" " foo.erase(it);\n" " return;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseReturn3() { check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (somecondition) {\n" " if (*it == 1)\n" " foo.erase(it);\n" " else\n" " *it = 0;\n" " return;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::vector foo;\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " if (a) {\n" " if (b)\n" " foo.erase(it);\n" " else\n" " *it = 0;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:9]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); } void eraseGoto() { check("void f()\n" "{\n" " for (std::vector::iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " goto abc;\n" " }\n" "bar:\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseAssign1() { check("void f()\n" "{\n" " for (std::vector::iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(it);\n" " it = foo.begin();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseAssign2() { check("void f(list &ints)\n" "{\n" " for (list::iterator it = ints.begin(); it != ints.end();) {\n" " if (*it == 123) {\n" " list::iterator copy = it;\n" " ++copy;\n" " ints.erase(it);\n" " it = copy;\n" " } else {\n" " it->second = 123;\n" " ++it;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseAssign3() { check("void f(std::list >& l) {\n" " std::list >::const_iterator i = l.begin();\n" " std::list::const_iterator j = (*i).begin();\n" " cout << *j << endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseAssign4() { check("void f(std::list data) {\n" " std::list::const_iterator it = data.begin();\n" " it = data.erase(it);\n" " it = data.erase(it);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(Data data) {\n" " std::list::const_iterator it = data.ints.begin();\n" " it = data.ints.erase(it);\n" " it = data.ints.erase(it);\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseAssignByFunctionCall() { check("void f(std::list >& l) {\n" " std::list::const_iterator i;\n" " bar(i);\n" " cout << *i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseErase() { check("void f(std::vector &ints)\n" "{\n" " std::vector::iterator iter;\n" " iter = ints.begin() + 2;\n" " ints.erase(iter);\n" " ints.erase(iter);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Invalid iterator: iter\n", errout.str()); } void eraseByValue() { check("void f()\n" "{\n" " std::set foo;\n" " for (std::set::iterator it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.erase(*it);\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Iterator 'it' becomes invalid when deleted by value from 'foo'\n", "", errout.str()); check("int f(std::set foo) {\n" " std::set::iterator it = foo.begin();\n" " foo.erase(*it);\n" " return *it;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (error) Iterator 'it' used after element has been erased.\n", errout.str()); check("void f()\n" "{\n" " std::set foo;\n" " std::set::iterator it = foo.begin();\n" " foo.erase(*it);\n" "}"); ASSERT_EQUALS("", errout.str()); // #5669 check("void f() {\n" " HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle );\n" " m_SetLoadedFactories.erase(*aIt);\n" " m_SetLoadedFactories.erase(aIt);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::list& m_ImplementationMap) {\n" " std::list::iterator aIt = m_ImplementationMap.find( xEle );\n" " m_ImplementationMap.erase(*aIt);\n" " m_ImplementationMap.erase(aIt);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Invalid iterator: aIt\n", errout.str()); check("void f(const std::list& m_ImplementationMap) {\n" " std::list::iterator aIt = m_ImplementationMap.find( xEle1 );\n" " std::list::iterator bIt = m_ImplementationMap.find( xEle2 );\n" " m_ImplementationMap.erase(*bIt);\n" " m_ImplementationMap.erase(aIt);\n" "}"); ASSERT_EQUALS("", errout.str()); } void eraseIf() { // #4816 check("void func(std::list strlist) {\n" " for (std::list::iterator str = strlist.begin(); str != strlist.end(); str++) {\n" " if (func2(*str)) {\n" " strlist.erase(str);\n" " if (strlist.empty())\n" " return;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Iterator 'str' used after element has been erased.\n", errout.str()); } void eraseOnVector() { check("void f(const std::vector& m_ImplementationMap) {\n" " std::vector::iterator aIt = m_ImplementationMap.find( xEle );\n" " m_ImplementationMap.erase(something(unknown));\n" // All iterators become invalidated when erasing from std::vector " m_ImplementationMap.erase(aIt);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) After erase(), the iterator 'aIt' may be invalid.\n", errout.str()); check("void f(const std::vector& m_ImplementationMap) {\n" " std::vector::iterator aIt = m_ImplementationMap.find( xEle );\n" " m_ImplementationMap.erase(*aIt);\n" // All iterators become invalidated when erasing from std::vector " m_ImplementationMap.erase(aIt);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Invalid iterator: aIt\n", errout.str()); check("void f(const std::vector& m_ImplementationMap) {\n" " std::vector::iterator aIt = m_ImplementationMap.find( xEle1 );\n" " std::vector::iterator bIt = m_ImplementationMap.find( xEle2 );\n" " m_ImplementationMap.erase(*bIt);\n" // All iterators become invalidated when erasing from std::vector " aIt = m_ImplementationMap.erase(aIt);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) After erase(), the iterator 'aIt' may be invalid.\n", errout.str()); } void pushback1() { check("void f(const std::vector &foo)\n" "{\n" " std::vector::const_iterator it = foo.begin();\n" " foo.push_back(123);\n" " *it;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) After push_back(), the iterator 'it' may be invalid.\n", errout.str()); } void pushback2() { check("void f()\n" "{\n" " std::vector::const_iterator it = foo.begin();\n" " foo.push_back(123);\n" " {\n" " int *it = &foo[0];\n" " *it = 456;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void pushback3() { check("void f()\n" "{\n" " std::vector foo;\n" " foo.push_back(10);\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.push_back(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) After push_back(), the iterator 'it' may be invalid.\n", errout.str()); } void pushback4() { check("void f()\n" "{\n" " std::vector ints;\n" " ints.push_back(1);\n" " int *first = &ints[0];\n" " ints.push_back(2);\n" " *first;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Invalid pointer 'first' after push_back().\n", errout.str()); } void pushback5() { check("void f()\n" "{\n" " std::vector::const_iterator i;\n" "\n" " for (i=v.begin(); i!=v.end(); ++i)\n" " {\n" " }\n" "\n" " for (i=rhs.v.begin(); i!=rhs.v.end(); ++i)\n" " {\n" " v.push_back(*i);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void pushback6() { // ticket #735 check("void f()\n" "{\n" " vector v;\n" " vector.push_back(1);\n" " vector.push_back(2);\n" " for (vector::iterator it = v.begin(); it != v.end(); ++it)\n" " {\n" " if (*it == 1)\n" " v.push_back(10);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) After push_back(), the iterator 'it' may be invalid.\n", errout.str()); check("void f()\n" "{\n" " std::vector v;\n" " vector.push_back(1);\n" " vector.push_back(2);\n" " for (std::vector::iterator it = v.begin(); it != v.end(); ++it)\n" " {\n" " if (*it == 1)\n" " v.push_back(10);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) After push_back(), the iterator 'it' may be invalid.\n", errout.str()); } void pushback7() { check("void f()\n" "{\n" " std::vector foo;\n" " foo.push_back(10);\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); it++)\n" " {\n" " foo.push_back(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) After push_back(), the iterator 'it' may be invalid.\n", errout.str()); } void pushback8() { check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::const_iterator end = ints.end();\n" " ints.push_back(10);\n" " std::vector::iterator it;\n" " unsigned int sum = 0;\n" " for (it = ints.begin(); it != end; ++it)\n" " {\n" " sum += *it;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) After push_back(), the iterator 'end' may be invalid.\n", errout.str()); } void pushback9() { check("struct A {\n" " std::vector ints;\n" "};\n" "\n" "void f()\n" "{\n" " std::vector ints;\n" " A a;\n" " std::vector::const_iterator i = ints.begin();\n" " std::vector::const_iterator e = ints.end();\n" " while (i != e)\n" " {\n" " a.ints.push_back(*i);\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void pushback10() { check("void f(std::vector &foo)\n" "{\n" " std::vector::const_iterator it = foo.begin();\n" " foo.reserve(100);\n" " *it = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) After reserve(), the iterator 'it' may be invalid.\n", errout.str()); // in loop check("void f()\n" "{\n" " std::vector foo;\n" " foo.push_back(10);\n" " std::vector::iterator it;\n" " for (it = foo.begin(); it != foo.end(); ++it)\n" " {\n" " foo.reserve(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) After reserve(), the iterator 'it' may be invalid.\n", errout.str()); } void pushback11() { // #2798 check("void f() {\n" " std::vector ints;\n" " std::vector::iterator it = ints.begin();\n" " if (it == ints.begin()) {\n" " ints.push_back(0);\n" " } else {\n" " ints.insert(it,0);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void pushback12() { // #4197 check("void foo(double s)\n" "{\n" " std::vector vec;\n" " for( std::vector::iterator it = vec.begin(); it != vec.end(); ++it )\n" " {\n" " vec.insert( it, s );\n" " for(unsigned int i = 0; i < 42; i++)\n" " {}\n" " *it;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) After insert(), the iterator 'it' may be invalid.\n" "[test.cpp:9]: (error) After insert(), the iterator 'it' may be invalid.\n", errout.str()); } void pushback13() { check("bool Preprocessor::ConcatenateIncludeName(SmallString<128> &FilenameBuffer, SourceLocation &End) {\n" " unsigned PreAppendSize = FilenameBuffer.size();\n" " FilenameBuffer.resize(PreAppendSize + CurTok.getLength());\n" " const char *BufPtr = &FilenameBuffer[PreAppendSize];\n" " return true;\n" "}"); ASSERT_EQUALS("", errout.str()); } void insert1() { check("void f(std::vector &ints)\n" "{\n" " std::vector::iterator iter = ints.begin() + 5;\n" " ints.insert(ints.begin(), 1);\n" " ++iter;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) After insert(), the iterator 'iter' may be invalid.\n", errout.str()); check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::iterator iter = ints.begin();\n" " ints.insert(iter, 1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::vector ints;\n" " std::vector::iterator iter = ints.begin();\n" " ints.insert(iter, 1);\n" " ints.insert(iter, 2);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) After insert(), the iterator 'iter' may be invalid.\n", errout.str()); check("void* f(const std::vector& bars) {\n" " std::vector::iterator i = bars.begin();\n" " bars.insert(Bar());\n" " void* v = &i->foo;\n" " return v;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) After insert(), the iterator 'i' may be invalid.\n", errout.str()); check("Foo f(const std::vector& bars) {\n" " std::vector::iterator i = bars.begin();\n" " bars.insert(Bar());\n" " return i->foo;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) After insert(), the iterator 'i' may be invalid.\n", errout.str()); check("void f(const std::vector& bars) {\n" " for(std::vector::iterator i = bars.begin(); i != bars.end(); ++i) {\n" " i = bars.insert(i, bar);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::vector& bars) {\n" " for(std::vector::iterator i = bars.begin(); i != bars.end(); ++i) {\n" " bars.insert(i, bar);\n" " i = bars.insert(i, bar);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) After insert(), the iterator 'i' may be invalid.\n" "[test.cpp:4]: (error) After insert(), the iterator 'i' may be invalid.\n", errout.str()); check("void* f(const std::vector& bars) {\n" " std::vector::iterator i = bars.begin();\n" " bars.insert(i, Bar());\n" " i = bars.insert(i, Bar());\n" " void* v = &i->foo;\n" " return v;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) After insert(), the iterator 'i' may be invalid.\n", errout.str()); } void insert2() { // Ticket: #2169 check("void f(std::vector &vec) {\n" " for(std::vector::iterator iter = vec.begin(); iter != vec.end(); ++iter)\n" " {\n" " vec.insert(iter, 0);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector &vec) {\n" " for(std::vector::iterator iter = vec.begin(); iter != vec.end(); ++iter)\n" " {\n" " if (*it == 0) {\n" " vec.insert(iter, 0);\n" " return;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } template static size_t getArraylength(const T(&)[n]) { return n; } void stlBoundaries1() { const std::string stlCont[] = { "list", "set", "multiset", "map", "multimap" }; for (size_t i = 0; i < getArraylength(stlCont); ++i) { check("void f()\n" "{\n" " std::" + stlCont[i] + "::iterator it;\n" " for (it = ab.begin(); it < ab.end(); ++it)\n" " ;\n" "}"); ASSERT_EQUALS_MSG("[test.cpp:4]: (error) Dangerous comparison using operator< on iterator.\n", errout.str(), stlCont[i]); } check("void f() {\n" " std::forward_list::iterator it;\n" " for (it = ab.begin(); ab.end() > it; ++it) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous comparison using operator< on iterator.\n", errout.str()); // #5926 no FP Dangerous comparison using operator< on iterator on std::deque check("void f() {\n" " std::deque::iterator it;\n" " for (it = ab.begin(); ab.end() > it; ++it) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void stlBoundaries2() { check("void f()\n" "{\n" " std::vector files;\n" " std::vector::const_iterator it;\n" " for (it = files.begin(); it < files.end(); it++) { }\n" " for (it = files.begin(); it < files.end(); it++) { };\n" "}"); ASSERT_EQUALS("", errout.str()); } void stlBoundaries3() { check("void f()\n" "{\n" " set files;\n" " set::const_iterator current;\n" " for (current = files.begin(); current != files.end(); ++current)\n" " {\n" " assert(*current < 100)\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " static set::const_iterator current;\n" " return 25 > current->bar;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid iterator 'current' used.\n", errout.str()); } void stlBoundaries4() { check("void f() {\n" " std::forward_list>>::iterator it;\n" " for (it = ab.begin(); ab.end() > it; ++it) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous comparison using operator< on iterator.\n", errout.str()); // don't crash check("void f() {\n" " if (list < 0) ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (list < 0) {\n" " std::forward_list>>::iterator it;\n" " for (it = ab.begin(); ab.end() > it; ++it) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous comparison using operator< on iterator.\n", errout.str()); } void stlBoundaries5() { check("class iterator { int foo(); };\n" "int foo() {\n" " iterator i;\n" " return i.foo();;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class iterator {\n" " Class operator*();\n" " iterator& operator++();\n" " int foo();\n" "};\n" "int foo() {\n" " iterator i;\n" " return i.foo();;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error, inconclusive) Invalid iterator 'i' used.\n", errout.str()); } void stlBoundaries6() { // #7106 check("void foo(std::vector& vec) {\n" " for (Function::iterator BB : vec) {\n" " for (int Inst : *BB)\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void if_find() { // --------------------------- // set::find // --------------------------- // error (simple) check("void f(std::set s)\n" "{\n" " if (s.find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (pointer) check("void f(std::set *s)\n" "{\n" " if ((*s).find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (pointer) check("void f(std::set *s)\n" "{\n" " if (s->find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (array-like pointer) check("void f(std::set *s)\n" "{\n" " if (s[0].find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (array) check("void f(std::set s [10])\n" "{\n" " if (s[0].find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (undefined length array) check("void f(std::set s [])\n" "{\n" " if (s[0].find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (vector) check("void f(std::vector > s)\n" "{\n" " if (s[0].find(12)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // error (assignment) check("void f(std::set s)\n" "{\n" " if (a || (x = s.find(12))) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // ok (simple) check("void f(std::set s)\n" "{\n" " if (s.find(123) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (pointer) check("void f(std::set *s)\n" "{\n" " if ((*s).find(12) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (array-like pointer) check("void f(std::set *s)\n" "{\n" " if (s[0].find(12) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (array) check("void f(std::set s [10])\n" "{\n" " if (s[0].find(123) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (undefined length array) check("void f(std::set s [])\n" "{\n" " if (s[0].find(123) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (vector) check("void f(std::vector > s)\n" "{\n" " if (s[0].find(123) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (assignment) check("void f(std::set s)\n" "{\n" " if (a || (x = s.find(12)) != s.end()) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (dereference, #6402) check("void f(std::set s) {\n" " if (s.find(12).member) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // --------------------------- // std::find // --------------------------- // error check("void f()\n" "{\n" " if (std::find(a,b,c)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Suspicious condition. The result of find() is an iterator, but it is not properly checked.\n", errout.str()); // ok check("void f()\n" "{\n" " if (std::find(a,b,c) != c) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok (less than comparison, #6217) check("void f(std::vector s)\n" "{\n" " if (std::find(a, b, c) < d) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3714 - segmentation fault for syntax error check("void f() {\n" " if (()) { }\n" "}"); // #3865 check("void f() {\n" " if ((std::find(a,b,c)) != b) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void if_str_find() { // error (simple) check("void f(const std::string &s)\n" "{\n" " if (s.find(\"abc\")) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Inefficient usage of string::find() in condition; string::compare() would be faster.\n", errout.str()); // error (pointer) check("void f(const std::string *s)\n" "{\n" " if ((*s).find(\"abc\")) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Inefficient usage of string::find() in condition; string::compare() would be faster.\n", errout.str()); // error (pointer) check("void f(const std::string *s)\n" "{\n" " if (s->find(\"abc\")) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Inefficient usage of string::find() in condition; string::compare() would be faster.\n", errout.str()); // error (vector) check("void f(const std::vector &s)\n" "{\n" " if (s[0].find(\"abc\")) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Inefficient usage of string::find() in condition; string::compare() would be faster.\n", errout.str()); // #3162 check("void f(const std::string& s1, const std::string& s2)\n" "{\n" " if ((!s1.empty()) && (0 == s1.find(s2))) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Inefficient usage of string::find() in condition; string::compare() would be faster.\n", errout.str()); // #4102 check("void f(const std::string &define) {\n" " if (define.find(\"=\") + 1U == define.size());\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::string a, std::string b) {\n" // #4480 " if (a.find(\"<\") < b.find(\">\")) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::string &s) {\n" " if (foo(s.find(\"abc\"))) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #7349 - std::string::find_first_of check("void f(const std::string &s) {\n" " if (s.find_first_of(\"abc\")==0) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void size1() { const char* code = "struct Fred {\n" " void foo();\n" " std::list x;\n" "};\n" "void Fred::foo()\n" "{\n" " if (x.size() == 0) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:7]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "std::list x;\n" "void f()\n" "{\n" " if (x.size() == 0) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (x.size() == 0) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (0 == x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (x.size() != 0) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (0 != x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (x.size() > 0) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (0 < x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (x.size() >= 1) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (x.size() < 1) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (1 <= x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (1 > x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " if (!x.size()) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " fun(x.size());\n" "}"); ASSERT_EQUALS("", errout.str()); code ="void f()\n" "{\n" " std::list x;\n" " fun(!x.size());\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); code = "void f()\n" "{\n" " std::list x;\n" " fun(a && x.size());\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #4039 " std::list x;\n" " fun(width % x.size() != 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4584 check("void f() {\n" " std::list x;\n" " if (foo + 1 > x.size()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::list x;\n" " if (x.size() < 1 + foo) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void size2() { const char* code = "struct Fred {\n" " std::list x;\n" "};\n" "struct Wilma {\n" " Fred f;\n" " void foo();\n" "};\n" "void Wilma::foo()\n" "{\n" " if (f.x.size() == 0) {}\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:10]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); } void size3() { const char* code = "namespace N {\n" " class Zzz {\n" " public:\n" " std::list x;\n" " };\n" "}\n" "using namespace N;\n" "Zzz * zzz;\n" "int main() {\n" " if (zzz->x.size() > 0) { }\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:10]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); code = "namespace N {\n" " class Zzz {\n" " public:\n" " std::list x;\n" " };\n" "}\n" "using namespace N;\n" "int main() {\n" " Zzz * zzz;\n" " if (zzz->x.size() > 0) { }\n" "}"; check(code, false, Standards::CPP03); ASSERT_EQUALS("[test.cpp:10]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check(code); ASSERT_EQUALS("", errout.str()); } void size4() { // #2652 - don't warn about vector/deque check("void f(std::vector &v) {\n" " if (v.size() > 0U) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::deque &v) {\n" " if (v.size() > 0U) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::array &a) {\n" " if (a.size() > 0U) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantCondition1() { check("void f(string haystack)\n" "{\n" " if (haystack.find(needle) != haystack.end())\n" " haystack.remove(needle);" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant checking of STL container element existence before removing it.\n", errout.str()); } void missingInnerComparison1() { check("void f(std::set &ints) {\n" " for (std::set::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " if (a) {\n" " it++;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Missing bounds check for extra iterator increment in loop.\n", errout.str()); check("void f(std::map &ints) {\n" " for (std::map::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " ++it->second;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison2() { check("void f(std::set &ints) {\n" " for (std::set::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " if (a) {\n" " it++;\n" " if (it == ints.end())\n" " return;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison3() { check("void f(std::set &ints) {\n" " for (std::set::iterator it = ints.begin(); it != ints.end(); ++it) {\n" " for (std::set::iterator it = ints2.begin(); it != ints2.end(); ++it)\n" " { }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison4() { check("function f1(std::list &l1) {\n" " for(std::list::iterator i = l1.begin(); i != l1.end(); i++) {\n" " if (*i == 44) {\n" " l1.insert(++i, 55);\n" " break;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("function f1(std::list &l1) {\n" " for(std::list::iterator i = l1.begin(); i != l1.end(); i++) {\n" " if (*i == 44) {\n" " l1.insert(++i, 55);\n" " return;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison5() { check("void f() {\n" " for(it = map1.begin(); it != map1.end(); it++) {\n" " str[i++] = (*it).first;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingInnerComparison6() { check("void f(std::string &s) {\n" " for(string::iterator it = s.begin(); it != s.end(); it++) {\n" " it = s.insert(++it, 0);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void cstr() { check("void f() {\n" " std::string errmsg;\n" " throw errmsg.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception.\n", errout.str()); check("const char *get_msg() {\n" " std::string errmsg;\n" " return errmsg.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("const char *get_msg() {\n" " std::ostringstream errmsg;\n" " return errmsg.str().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("const char *get_msg() {\n" " std::string errmsg;\n" " return std::string(\"ERROR: \" + errmsg).c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("const char *get_msg() {\n" " std::string errmsg;\n" " return (\"ERROR: \" + errmsg).c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("const char *get_msg() {\n" " std::string errmsg;\n" " return (\"ERROR: \" + std::string(\"crash me\")).c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("void f() {\n" " std::ostringstream errmsg;\n" " const char *c = errmsg.str().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("std::string f();\n" "\n" "void foo() {\n" " const char *c = f().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("class Foo {\n" " const char *f();\n" "};\n" "const char *Foo::f() {\n" " std::string s;\n" " return s.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("class Foo {\n" " std::string GetVal() const;\n" "};\n" "const char *f() {\n" " Foo f;\n" " return f.GetVal().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("const char* foo() {\n" " static std::string text;\n" " text = \"hello world\\n\";\n" " return text.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); // #3427 // Implicit conversion back to std::string check("std::string get_msg() {\n" " std::string errmsg;\n" " return errmsg.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Returning the result of c_str() in a function that returns std::string is slow and redundant.\n", errout.str()); check("const std::string& get_msg() {\n" " std::string errmsg;\n" " return errmsg.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Returning the result of c_str() in a function that returns std::string is slow and redundant.\n", errout.str()); check("class Foo {\n" " std::string GetVal() const;\n" "};\n" "std::string f() {\n" " Foo f;\n" " return f.GetVal().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Returning the result of c_str() in a function that returns std::string is slow and redundant.\n", errout.str()); check("std::string get_msg() {\n" " std::string errmsg;\n" " return errmsg;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string get_msg() {\n" // #3678 " MyStringClass errmsg;\n" " return errmsg.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void Foo1(const std::string& str) {}\n" "void Foo2(char* c, const std::string str) {}\n" "void Foo3(std::string& rstr) {}\n" "void Foo4(std::string str, const std::string& str) {}\n" "void Bar() {\n" " std::string str = \"bar\";\n" " std::stringstream ss(\"foo\");\n" " Foo1(str);\n" " Foo1(str.c_str());\n" " Foo2(str.c_str(), str);\n" " Foo2(str.c_str(), str.c_str());\n" " Foo3(str.c_str());\n" " Foo4(str, str);\n" " Foo4(str.c_str(), str);\n" " Foo4(str, str.c_str());\n" " Foo4(ss.str(), ss.str().c_str());\n" " Foo4(str.c_str(), str.c_str());\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 1 is slow and redundant.\n" "[test.cpp:11]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 2 is slow and redundant.\n" "[test.cpp:14]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 1 is slow and redundant.\n" "[test.cpp:15]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 2 is slow and redundant.\n" "[test.cpp:16]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 2 is slow and redundant.\n" "[test.cpp:17]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 1 is slow and redundant.\n" "[test.cpp:17]: (performance) Passing the result of c_str() to a function that takes std::string as argument no. 2 is slow and redundant.\n", errout.str()); check("void Foo1(const std::string& str) {}\n" "void Foo2(char* c, const std::string str) {}\n" "void Bar() {\n" " std::string str = \"bar\";\n" " Foo1(str, foo);\n" // Don't crash " Foo2(str.c_str());\n" // Don't crash "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {\n" " void func(std::string str) const {}\n" " static void sfunc(std::string str) {}\n" "};\n" "void func(std::string str) {}\n" "void Bar() {\n" " std::string str = \"bar\";\n" " Foo foo;\n" " func(str.c_str());\n" " Foo::sfunc(str.c_str());\n" " foo.func(str.c_str());\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (performance) Passing the result of c_str() to a function that takes std::string as argument 1 is slow and redundant.\n" "[test.cpp:10]: (performance) Passing the result of c_str() to a function that takes std::string as argument 1 is slow and redundant.\n", "", errout.str()); check("void svgFile(const std::string &content, const std::string &fileName, const double end = 1000., const double start = 0.);\n" "void Bar(std::string filename) {\n" " std::string str = \"bar\";\n" " std::ofstream svgFile(filename.c_str(), std::ios::trunc);\n" " svgFile << \"test\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("void Foo(const char* p) {}\n" "void Foo(const std::string& str) {Foo(str.c_str());}\n" // Overloaded "void Bar() {\n" " std::string str = \"bar\";\n" " Foo(str);\n" " Foo(str.c_str());\n" "}"); ASSERT_EQUALS("", errout.str()); check("int atoi(const std::string& str) {\n" // #3729: Don't suggest recursive call " return atoi(str.c_str());\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string hello()\n" "{\n" " return \"hello\";\n" "}\n" "\n" "const char *f()\n" "{\n" " return hello().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("class Fred {\n" " std::string hello();\n" " const char *f();\n" "};\n" "std::string Fred::hello()\n" "{\n" " return \"hello\";\n" "}\n" "const char *Fred::f()\n" "{\n" " return hello().c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); // #4183 - using MyStringClass.c_str() check("void a(const std::string &str);\n" "\n" "void b() {\n" " MyStringClass s;\n" " a(s.c_str());\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string Format(const char * name) {\n" // #4938 " return String::Format(\"%s:\", name).c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); // #7480 check("struct InternalMapInfo {\n" " std::string author;\n" "};\n" "const char* GetMapAuthor(int index) {\n" " const InternalMapInfo* mapInfo = &internal_getMapInfo;\n" " return mapInfo->author.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct InternalMapInfo {\n" " std::string author;\n" "};\n" "std::string GetMapAuthor(int index) {\n" " const InternalMapInfo* mapInfo = &internal_getMapInfo;\n" " return mapInfo->author.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Returning the result of c_str() in a function that returns std::string is slow and redundant.\n", errout.str()); check("struct InternalMapInfo {\n" " std::string author;\n" "};\n" "const char* GetMapAuthor(int index) {\n" " const InternalMapInfo mapInfo = internal_getMapInfo;\n" " return mapInfo.author.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); check("struct S {\n" // #7656 " std::string data;\n" "};\n" "const S& getS();\n" "const char* test() {\n" " const struct S &s = getS();\n" " return s.data.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" // #7930 " std::string data;\n" "};\n" "const char* test() {\n" " S s;\n" " std::string &ref = s.data;\n" " return ref.c_str();\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n", errout.str()); } void autoPointer() { // ticket 2846 check("void f()\n" "{\n" " std::vector vec;\n" " vec.push_back(new int(3));\n" " std::auto_ptr ret(vec[0]);\n" "};"); ASSERT_EQUALS("", errout.str()); // ticket 2839 check("template \n" "class Guarded\n" "{\n" " typedef std::auto_ptr WriteGuardType;\n" " virtual WriteGuardType getWriteGuard(bool enabledGuard = true);\n" "};\n" "class SafeSharedMemory : public Guarded\n" "{\n" "};"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " auto_ptr< ns1:::MyClass > y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " auto_ptr p2;\n" " p2 = new T;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::vector< std::auto_ptr< ns1::MyClass> > v;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) You can randomly lose access to pointers if you store 'auto_ptr' pointers in an STL container.\n", errout.str()); check("void foo()\n" "{\n" " std::vector< auto_ptr< MyClass> > v;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) You can randomly lose access to pointers if you store 'auto_ptr' pointers in an STL container.\n", errout.str()); check("void foo()\n" "{\n" " int *i = new int;\n" " auto_ptr x(i);\n" " auto_ptr y;\n" " y = x;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Copying 'auto_ptr' pointer to another does not create two equal objects since one has lost its ownership of the pointer.\n", errout.str()); check("std::auto_ptr function();\n" "int quit;" "void f() { quit = true; }\n" ); ASSERT_EQUALS("", errout.str()); // ticket #748 check("void f()\n" "{\n" " T* var = new T[10];\n" " auto_ptr p2( var );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " foo::bar::baz* var = new foo::bar::baz[10];\n" " auto_ptr p2( var );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2( new T[] );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2( new T[5] );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p(new foo::bar[10]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2;\n" " p2.reset( new T[] );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2( new T[][] );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2;\n" " p2 = new T[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2;\n" " p2 = new T::B[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2;\n" " p2.reset( new T[10] );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); check("void f()\n" "{\n" " auto_ptr p2;\n" " p2.reset( new T::B[10] );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n", errout.str()); // ticket #2887 (infinite loop) check("A::A(std::auto_ptr e){}"); ASSERT_EQUALS("", errout.str()); // ticket #4390 check("auto_ptr CreateRegistryStringStorage() {\n" " return auto_ptr(new RegistryConnectionStringStorage());\n" "}\n" "\n" "void LookupWindowsUserAccountName() {\n" " auto_ptr_array domainName(new char[42]);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #749 check("int main()\n" "{\n" " int *i = malloc(sizeof(int));\n" " auto_ptr x(i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); check("int main()\n" "{\n" " auto_ptr x((int*)malloc(sizeof(int)*4));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); check("int main()\n" "{\n" " auto_ptr b(static_cast(malloc(sizeof(int)*4)));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); check("int main()\n" "{\n" " auto_ptr x = (int*)malloc(sizeof(int)*4);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); check("int main()\n" "{\n" " auto_ptr x = static_cast(malloc(sizeof(int)*4));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); check("int main()\n" "{\n" " auto_ptr x;\n" " x.reset((int*)malloc(sizeof(int)*4));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); check("int main()\n" "{\n" " auto_ptr x;\n" " x.reset(static_cast(malloc(sizeof(int)*4)));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function 'malloc'.\n", errout.str()); } void uselessCalls() { check("void f()\n" "{\n" " string s1, s2;\n" " s1.swap(s2);\n" " s2.swap(s2);\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::string s1, s2;\n" " s1.swap(s2);\n" " s2.swap(s2);\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (performance) It is inefficient to swap a object with itself by calling 's2.swap(s2)'\n", errout.str()); check("void f()\n" "{\n" " std::string s1, s2;\n" " s1.compare(s2);\n" " s2.compare(s2);\n" " s1.compare(s2.c_str());\n" " s1.compare(0, s1.size(), s1);\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) It is inefficient to call 's2.compare(s2)' as it always returns 0.\n", errout.str()); // #7370 False positive uselessCallsCompare on unknown type check("class ReplayIteratorImpl{\n" " int Compare(ReplayIteratorImpl* other) {\n" " int cmp;\n" " int ret = cursor_->compare(cursor_, other->cursor_, &cmp);\n" " return (cmp);\n" " }\n" " WT_CURSOR *cursor_;\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int x=1;\n" " std::string s1, s2;\n" " s1 = s1.substr();\n" " s2 = s1.substr(x);\n" " s1 = s2.substr(0, x);\n" " s1 = s2.substr(0,std::string::npos);\n" " s1 = s2.substr(x+5-n, 0);\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (performance) Ineffective call of function \'substr\' because it returns a copy of " "the object. Use operator= instead.\n" "[test.cpp:8]: (performance) Ineffective call of function \'substr\' because it returns a copy of " "the object. Use operator= instead.\n" "[test.cpp:9]: (performance) Ineffective call of function \'substr\' because it returns an empty string.\n", errout.str()); check("void f()\n" "{\n" " int x=1;\n" " string s1, s2;\n" " s1 = s1.substr();\n" " s2 = s1.substr(x);\n" " s1 = s2.substr(0, x);\n" " s1 = s2.substr(0,std::string::npos);\n" " s1 = s2.substr(x+5-n, 0);\n" "};"); ASSERT_EQUALS("", errout.str()); check("int main()\n" "{\n" " std::string str = \"a1b1\";\n" " return str.find(str[1], 2);\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool foo(std::vector& v) {\n" " v.empty();\n" " return v.empty();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead?\n", errout.str()); check("void f() {\n" // #4938 " OdString str;\n" " str.empty();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #4032 " const std::string greeting(\"Hello World !!!\");\n" " const std::string::size_type npos = greeting.rfind(\" \");\n" " if (npos != std::string::npos)\n" " std::cout << greeting.substr(0, npos) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::vector a;\n" " std::remove(a.begin(), a.end(), val);\n" " std::remove_if(a.begin(), a.end(), val);\n" " std::unique(a.begin(), a.end(), val);\n" " x = std::remove(a.begin(), a.end(), val);\n" " a.erase(std::remove(a.begin(), a.end(), val));\n" " std::remove(\"foo.txt\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of std::remove() ignored. Elements remain in container.\n" "[test.cpp:4]: (warning) Return value of std::remove_if() ignored. Elements remain in container.\n" "[test.cpp:5]: (warning) Return value of std::unique() ignored. Elements remain in container.\n", errout.str()); // #4431 - fp check("bool f() {\n" " return x ? true : (y.empty());\n" "}"); ASSERT_EQUALS("", errout.str()); } void stabilityOfChecks() { // Stability test: 4684 cppcheck crash in template function call. check("template\n" "class EffectivityRangeData {};\n" "template\n" "class EffectivityRange {\n" " void unite() {\n" " x < vector < EffectivityRangeData> >();\n" " EffectivityRange er;\n" " }\n" " void shift() { EffectivityRangeData::iterator it; } \n" "};\n"); ASSERT_EQUALS("", errout.str()); } void dereferenceInvalidIterator() { // Test simplest "if" with && case check("void foo(std::string::iterator& i) {\n" " if (std::isalpha(*i) && i != str.end()) {\n" " std::cout << *i;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); check("void foo(std::string::iterator& i) {\n" " if(foo) { bar(); }\n" " else if (std::isalpha(*i) && i != str.end()) {\n" " std::cout << *i;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test suggested correction doesn't report an error check("void foo(std::string::iterator& i) {\n" " if (i != str.end() && std::isalpha(*i)) {\n" " std::cout << *i;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Test "while" with "&&" case check("void foo(std::string::iterator& i) {\n" " while (std::isalpha(*i) && i != str.end()) {\n" " std::cout << *i;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); check("void foo(std::string::iterator& i) {\n" " do {\n" " std::cout << *i;\n" " i ++;\n" " } while (std::isalpha(*i) && i != str.end());\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test "while" with "||" case check("void foo(std::string::iterator& i) {\n" " while (!(!std::isalpha(*i) || i == str.end())) {\n" " std::cout << *i;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test fix for "while" with "||" case check("void foo(std::string::iterator& i) {\n" " while (!(i == str.end() || !std::isalpha(*i))) {\n" " std::cout << *i;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Test "for" with "&&" case check("void foo(std::string::iterator& i) {\n" " for (; std::isalpha(*i) && i != str.end() ;) {\n" " std::cout << *i;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test "for" with "||" case check("void foo(std::string::iterator& i) {\n" " for (; std::isalpha(*i) || i == str.end() ;) {\n" " std::cout << *i;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test that a dereference outside the condition part of a "for" // loop does not result in a false positive check("void foo(std::string::iterator& i) {\n" " for (char c = *i; isRunning && i != str.end() ;) {\n" " std::cout << c;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Test that other "&&" terms in the condition don't invalidate the check check("void foo(char* c, std::string::iterator& i) {\n" " if (*c && std::isalpha(*i) && i != str.end()) {\n" " std::cout << *i;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test that dereference of different variable doesn't trigger a false positive check("void foo(const char* c, std::string::iterator& i) {\n" " if (std::isalpha(*c) && i != str.end()) {\n" " std::cout << *c;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Test case involving "rend()" instead of "end()" check("void foo(std::string::iterator& i) {\n" " while (std::isalpha(*i) && i != str.rend()) {\n" " std::cout << *i;\n" " i ++;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible dereference of an invalid iterator: i\n", errout.str()); // Test that mixed "&&" and "||" don't result in a false positive check("void foo(std::string::iterator& i) {\n" " if ((i == str.end() || *i) || (isFoo() && i != str.end())) {\n" " std::cout << \"foo\";\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void dereferenceInvalidIterator2() { // Self-implemented iterator class check("class iterator {\n" "public:\n" " CCommitPointer m_ptr;\n" " iterator() {}\n" " CCommitPointer& operator*() {\n" " return m_ptr;\n" " }\n" " CCommitPointer* operator->() {\n" " return &m_ptr;\n" " }\n" " iterator& operator++() {\n" " ++m_ptr.m_place;\n" " return *this;\n" " }\n" " }; \n" " iterator begin() {\n" " iterator it; \n" " it->m_place = 0;\n" " return it; \n" "}\n"); ASSERT_EQUALS("[test.cpp:18]: (error, inconclusive) Invalid iterator 'it' used.\n", errout.str()); } void readingEmptyStlContainer() { check("void f() {\n" " std::map CMap;\n" " std::string strValue = CMap[1]; \n" " std::cout << strValue << CMap.size() << std::endl;\n" "}\n",true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'CMap'\n", errout.str()); check("void f() {\n" " std::map CMap;\n" " std::string strValue = CMap[1];" "}\n",true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'CMap'\n", errout.str()); check("void f() {\n" " std::map CMap;\n" " CMap[1] = \"123\";\n" " std::string strValue = CMap[1];" "}\n",true); ASSERT_EQUALS("", errout.str()); check("std::vector f() {\n" " try {\n" " std::vector Vector;\n" " std::vector v2 = Vector;\n" " std::string strValue = v2[1]; \n" // Do not complain here - this is a consecutive fault of the line above. " }\n" " return Vector;\n" "}\n",true); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Reading from empty STL container 'Vector'\n", errout.str()); check("Vector f() {\n" " try {\n" " std::vector Vector;\n" " Vector.push_back(\"123\");\n" " std::vector v2 = Vector;\n" " std::string strValue = v2[0]; \n" " }\n" " return Vector;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::map mymap;\n" " mymap[\"Bakery\"] = \"Barbara\";\n" " std:string bakery_name = mymap[\"Bakery\"];\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::vector v;\n" " v.insert(1);\n" " int i = v[0];\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::vector v;\n" " initialize(v);\n" " int i = v[0];\n" "}", true); ASSERT_EQUALS("", errout.str()); check("char f() {\n" " std::string s(foo);\n" " return s[0];\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::vector v = foo();\n" " if(bar) v.clear();\n" " int i = v.find(foobar);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(std::set v) {\n" " v.clear();\n" " int i = v.find(foobar);\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'v'\n", errout.str()); check("void f(std::set v) {\n" " v.clear();\n" " v.begin();\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'v'\n", errout.str()); check("void f(std::set v) {\n" " v.clear();\n" " *v.begin();\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'v'\n", errout.str()); check("void f(std::set v) {\n" " v.clear();\n" " for(auto i = v.cbegin();\n" " i != v.cend(); ++i) {}\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Reading from empty STL container 'v'\n", errout.str()); check("void f(std::set v) {\n" " v.clear();\n" " foo(v.begin());\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::map CMap;\n" " std::string strValue = CMap[1];\n" " std::string strValue2 = CMap[1];\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'CMap'\n", errout.str()); // #4306 check("void f(std::vector v) {\n" " v.clear();\n" " for(int i = 0; i < v.size(); i++) { cout << v[i]; }\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container 'v'\n", errout.str()); // #7449 - nonlocal vector check("std::vector v;\n" "void f() {\n" " v.clear();\n" " dostuff()\n" " if (v.empty()) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" "void f() {\n" " v.clear();\n" " if (v.empty()) { }\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Reading from empty STL container 'v'\n", errout.str()); // #6663 check("void foo() {\n" " std::set container;\n" " while (container.size() < 5)\n" " container.insert(22);\n" "}", true); ASSERT_EQUALS("", errout.str()); // #6679 check("class C {\n" " C() {\n" " switch (ret) {\n" " case 1:\n" " vec.clear();\n" " break;\n" " case 2:\n" " if (vec.empty())\n" " ;\n" " break;\n" " }\n" " }\n" " std::vector vec;\n" "};", true); ASSERT_EQUALS("", errout.str()); check("class C {\n" " C() {\n" " switch (ret) {\n" " case 1:\n" " vec.clear();\n" " case 2:\n" " if (vec.empty())\n" " ;\n" " break;\n" " }\n" " }\n" " std::vector vec;\n" "};", true); ASSERT_EQUALS("", errout.str()); check("class C {\n" " C() {\n" " switch (ret) {\n" " case 1:\n" " vec.clear();\n" " if (vec.empty())\n" " ;\n" " break;\n" " }\n" " }\n" " std::vector vec;\n" "};", true); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Reading from empty STL container 'vec'\n", errout.str()); // #7560 check("std::vector test;\n" "std::vector::iterator it;\n" "void Reset() {\n" " test.clear();\n" " it = test.end();\n" "}"); ASSERT_EQUALS("", errout.str()); // #8055 check("int main() {\n" " std::string str;\n" " auto l = [&]() {\n" " if (str[0] == 'A')\n" " std::cout << \"!\";\n" " }\n" " str = \"A\";\n" " l();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::vector &v) {\n" " for (const std::string& s : v) {\n" " if (s.find(x) != string::npos) {}\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestStl) cppcheck-1.82/test/teststring.cpp000066400000000000000000000546541322667425100171430ustar00rootroot00000000000000/* * 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 "checkstring.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestString : public TestFixture { public: TestString() : TestFixture("TestString") { } private: Settings settings; void run() { settings.addEnabled("warning"); settings.addEnabled("style"); TEST_CASE(stringLiteralWrite); TEST_CASE(alwaysTrueFalseStringCompare); TEST_CASE(suspiciousStringCompare); TEST_CASE(suspiciousStringCompare_char); TEST_CASE(strPlusChar1); // "/usr" + '/' TEST_CASE(strPlusChar2); // "/usr" + ch TEST_CASE(strPlusChar3); // ok: path + "/sub" + '/' TEST_CASE(sprintf1); // Dangerous usage of sprintf TEST_CASE(sprintf2); TEST_CASE(sprintf3); TEST_CASE(sprintf4); // struct member TEST_CASE(incorrectStringCompare); TEST_CASE(deadStrcmp); } 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 char variable usage.. CheckString checkString(&tokenizer, &settings, this); checkString.runChecks(&tokenizer, &settings, this); tokenizer.simplifyTokenList2(); checkString.runSimplifiedChecks(&tokenizer, &settings, this); } void stringLiteralWrite() { check("void f() {\n" " char *abc = \"abc\";\n" " abc[0] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \"abc\" directly or indirectly is undefined behaviour.\n", errout.str()); check("void f() {\n" " char *abc = \"abc\";\n" " *abc = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \"abc\" directly or indirectly is undefined behaviour.\n", errout.str()); check("void f() {\n" " QString abc = \"abc\";\n" " abc[0] = 'a';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo_FP1(char *p) {\n" " p[1] = 'B';\n" "}\n" "void foo_FP2(void) {\n" " char* s = \"Y\";\n" " foo_FP1(s);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (error) Modifying string literal \"Y\" directly or indirectly is undefined behaviour.\n", errout.str()); check("void foo_FP1(char *p) {\n" " p[1] = 'B';\n" "}\n" "void foo_FP2(void) {\n" " char s[10] = \"Y\";\n" " foo_FP1(s);\n" "}"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueFalseStringCompare() { check("void f() {\n" " if (strcmp(\"A\",\"A\")){}\n" " if (strncmp(\"A\",\"A\",1)){}\n" " if (strcasecmp(\"A\",\"A\")){}\n" " if (strncasecmp(\"A\",\"A\",1)){}\n" " if (memcmp(\"A\",\"A\",1)){}\n" " if (strverscmp(\"A\",\"A\")){}\n" " if (bcmp(\"A\",\"A\",1)){}\n" " if (wcsncasecmp(L\"A\",L\"A\",1)){}\n" " if (wcsncmp(L\"A\",L\"A\",1)){}\n" " if (wmemcmp(L\"A\",L\"A\",1)){}\n" " if (wcscmp(L\"A\",L\"A\")){}\n" " if (wcscasecmp(L\"A\",L\"A\")){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:3]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:4]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:5]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:6]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:7]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:8]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:9]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:10]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:11]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:12]: (warning) Unnecessary comparison of static strings.\n" "[test.cpp:13]: (warning) Unnecessary comparison of static strings.\n", errout.str()); // avoid false positives when the address is modified #6415 check("void f(void *p, int offset) {\n" " if (!memcmp(p, p + offset, 42)){}\n" " if (!memcmp(p + offset, p, 42)){}\n" " if (!memcmp(offset + p, p, 42)){}\n" " if (!memcmp(p, offset + p, 42)){}\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid false positives when the address is modified #6415 check("void f(char *c, int offset) {\n" " if (!memcmp(c, c + offset, 42)){}\n" " if (!memcmp(c + offset, c, 42)){}\n" " if (!memcmp(offset + c, c, 42)){}\n" " if (!memcmp(c, offset + c, 42)){}\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid false positives when the address is modified #6415 check("void f(std::string s, int offset) {\n" " if (!memcmp(s.c_str(), s.c_str() + offset, 42)){}\n" " if (!memcmp(s.c_str() + offset, s.c_str(), 42)){}\n" " if (!memcmp(offset + s.c_str(), s.c_str(), 42)){}\n" " if (!memcmp(s.c_str(), offset + s.c_str(), 42)){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main()\n" "{\n" " if (strcmp(\"00FF00\", \"00FF00\") == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check("void f() {\n" " if (strcmp($\"00FF00\", \"00FF00\") == 0) {}" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if ($strcmp(\"00FF00\", \"00FF00\") == 0) {}" "}"); ASSERT_EQUALS("", errout.str()); check("int main()\n" "{\n" " if (stricmp(\"hotdog\",\"HOTdog\") == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check("int main()\n" "{\n" " if (QString::compare(\"Hamburger\", \"Hotdog\") == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check("int main()\n" "{\n" " if (QString::compare(argv[2], \"hotdog\") == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("", errout.str()); check("int main()\n" "{\n" " if (strncmp(\"hotdog\",\"hotdog\", 6) == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check("int foo(const char *buf)\n" "{\n" " if (strcmp(buf, buf) == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of identical string variables.\n", errout.str()); check("int foo(const std::string& buf)\n" "{\n" " if (stricmp(buf.c_str(), buf.c_str()) == 0)" " {" " std::cout << \"Equal\";" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of identical string variables.\n", errout.str()); check("int main() {\n" " if (\"str\" == \"str\") {\n" " std::cout << \"Equal\";\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check("int main() {\n" " if (\"str\" != \"str\") {\n" " std::cout << \"Equal\";\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check("int main() {\n" " if (a+\"str\" != \"str\"+b) {\n" " std::cout << \"Equal\";\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void suspiciousStringCompare() { check("bool foo(char* c) {\n" " return c == \"x\";\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead?\n", errout.str()); check("bool foo(const char* c) {\n" " return \"x\" == c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead?\n", errout.str()); check("bool foo(char* c) {\n" " return foo+\"x\" == c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool foo(char* c) {\n" " return \"x\" == c+foo;\n" "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("bool foo(char* c) {\n" " return \"x\" == c+foo;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead?\n", errout.str()); check("bool foo(Foo c) {\n" " return \"x\" == c.foo;\n" "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("bool foo(Foo c) {\n" " return \"x\" == c.foo;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (warning) String literal compared with variable 'c.foo'. Did you intend to use strcmp() instead?\n", errout.str()); check("bool foo(const std::string& c) {\n" " return \"x\" == c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool foo(const Foo* c) {\n" " return \"x\" == c->bar();\n" // #4314 "}"); ASSERT_EQUALS("", errout.str()); // Ticket #4257 check("bool foo() {\n" "MyString *str=Getter();\n" "return *str==\"bug\"; }\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (warning) String literal compared with variable '*str'. Did you intend to use strcmp() instead?\n", errout.str()); // Ticket #4257 check("bool foo() {\n" "MyString *str=Getter();\n" "return *str==\"bug\"; }"); ASSERT_EQUALS("", errout.str()); // Ticket #4257 check("bool foo() {\n" "MyString **str=OtherGetter();\n" "return *str==\"bug\"; }"); TODO_ASSERT_EQUALS("[test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead?\n", "", errout.str()); // Ticket #4257 check("bool foo() {\n" "MyString str=OtherGetter2();\n" "return &str==\"bug\"; }"); TODO_ASSERT_EQUALS("[test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead?\n", "", errout.str()); // Ticket #5734 check("int foo(char c) {\n" "return c == '4';}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("int foo(char c) {\n" "return c == '4';}", "test.c"); ASSERT_EQUALS("", errout.str()); check("int foo(char c) {\n" "return c == \"42\"[0];}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("int foo(char c) {\n" "return c == \"42\"[0];}", "test.c"); ASSERT_EQUALS("", errout.str()); // 5639 String literal compared with char buffer in a struct check("struct Example {\n" " char buffer[200];\n" "};\n" "void foo() {\n" " struct Example example;\n" " if (example.buffer == \"test\") ;\n" "}\n", "test.cpp"); ASSERT_EQUALS("[test.cpp:6]: (warning) String literal compared with variable 'example.buffer'. Did you intend to use strcmp() instead?\n", errout.str()); check("struct Example {\n" " char buffer[200];\n" "};\n" "void foo() {\n" " struct Example example;\n" " if (example.buffer == \"test\") ;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:6]: (warning) String literal compared with variable 'example.buffer'. Did you intend to use strcmp() instead?\n", errout.str()); } void suspiciousStringCompare_char() { check("bool foo(char* c) {\n" " return c == 'x';\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it?\n", errout.str()); check("bool foo(char* c) {\n" " return '\\0' != c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it?\n", errout.str()); check("bool foo(char c) {\n" " return c == '\\0';\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool foo(char c) {\n" " return c == 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool foo(char* c) {\n" " return *c == 0;\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); check("bool foo(char* c) {\n" " return *c == 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool foo(Foo* c) {\n" " return 0 == c->x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* c) {\n" " if(c == '\\0') bar();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it?\n", errout.str()); check("void f() {\n" " struct { struct { char *str; } x; } a;\n" " return a.x.str == '\\0';" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Char literal compared with pointer 'a.x.str'. Did you intend to dereference it?\n", errout.str()); check("void f() {\n" " struct { struct { char *str; } x; } a;\n" " return *a.x.str == '\\0';" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf1() { check("void foo()\n" "{\n" " char buf[100];\n" " sprintf(buf,\"%s\",buf);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf().\n", errout.str()); } void sprintf2() { check("void foo()\n" "{\n" " char buf[100];\n" " sprintf(buf,\"%i\",sizeof(buf));\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf3() { check("void foo()\n" "{\n" " char buf[100];\n" " sprintf(buf,\"%i\",sizeof(buf));\n" " if (buf[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf4() { check("struct A\n" "{\n" " char filename[128];\n" "};\n" "\n" "void foo()\n" "{\n" " const char* filename = \"hello\";\n" " struct A a;\n" " snprintf(a.filename, 128, \"%s\", filename);\n" "}"); ASSERT_EQUALS("", errout.str()); } void strPlusChar1() { // Strange looking pointer arithmetic.. check("void foo()\n" "{\n" " const char *p = \"/usr\" + '/';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Unusual pointer arithmetic. A value of type 'char' is added to a string literal.\n", errout.str()); } void strPlusChar2() { // Strange looking pointer arithmetic.. check("void foo()\n" "{\n" " char ch = 1;\n" " const char *p = ch + \"/usr\";\n" "}"); ASSERT_EQUALS("", errout.str()); // Strange looking pointer arithmetic.. check("void foo()\n" "{\n" " int i = 1;\n" " const char* psz = \"Bla\";\n" " const std::string str = i + psz;\n" "}"); ASSERT_EQUALS("", errout.str()); } void strPlusChar3() { // Strange looking pointer arithmetic.. check("void foo()\n" "{\n" " std::string temp = \"/tmp\";\n" " std::string path = temp + '/' + \"sub\" + '/';\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectStringCompare() { check("int f() {\n" " return test.substr( 0 , 4 ) == \"Hello\" ? : 0 : 1 ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) String literal \"Hello\" doesn't match length argument for substr().\n", errout.str()); check("int f() {\n" " return test.substr( 0 , 5 ) == \"Hello\" ? : 0 : 1 ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return \"Hello\" == test.substr( 0 , 4 ) ? : 0 : 1 ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) String literal \"Hello\" doesn't match length argument for substr().\n", errout.str()); check("int f() {\n" " return \"Hello\" == foo.bar().z[1].substr(i+j*4, 4) ? : 0 : 1 ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) String literal \"Hello\" doesn't match length argument for substr().\n", errout.str()); check("int f() {\n" " return \"Hello\" == test.substr( 0 , 5 ) ? : 0 : 1 ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " if (\"Hello\") { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of string literal \"Hello\" to bool always evaluates to true.\n", errout.str()); check("int f() {\n" " if (\"Hello\" && test) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of string literal \"Hello\" to bool always evaluates to true.\n", errout.str()); check("int f() {\n" " if (test && \"Hello\") { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of string literal \"Hello\" to bool always evaluates to true.\n", errout.str()); check("int f() {\n" " while (\"Hello\") { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of string literal \"Hello\" to bool always evaluates to true.\n", errout.str()); check("int f() {\n" " assert (test || \"Hello\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of string literal \"Hello\" to bool always evaluates to true.\n", errout.str()); check("int f() {\n" " assert (test && \"Hello\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " assert (\"Hello\" || test);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of string literal \"Hello\" to bool always evaluates to true.\n", errout.str()); check("int f() {\n" " assert (\"Hello\" && test);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " BOOST_ASSERT (\"Hello\" && test);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return f2(\"Hello\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void deadStrcmp() { check("void f(const char *str) {\n" " if (strcmp(str, \"abc\") == 0 || strcmp(str, \"def\")) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) The expression 'strcmp(str,\"def\") != 0' is suspicious. It overlaps 'strcmp(str,\"abc\") == 0'.\n", errout.str()); check("struct X {\n" " char *str;\n" "};\n" "\n" "void f(const struct X *x) {\n" " if (strcmp(x->str, \"abc\") == 0 || strcmp(x->str, \"def\")) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (warning) The expression 'strcmp(x->str,\"def\") != 0' is suspicious. It overlaps 'strcmp(x->str,\"abc\") == 0'.\n", errout.str()); } }; REGISTER_TEST(TestString) cppcheck-1.82/test/testsuite.cpp000066400000000000000000000246601322667425100167600ustar00rootroot00000000000000/* * 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 "testsuite.h" #include "options.h" #include "redirect.h" #include #include #include #include std::ostringstream errout; std::ostringstream output; /** * TestRegistry **/ class TestRegistry { private: std::list _tests; public: static TestRegistry &theInstance() { static TestRegistry testreg; return testreg; } void addTest(TestFixture *t) { _tests.push_back(t); } const std::list &tests() const { return _tests; } }; /** * TestFixture **/ std::ostringstream TestFixture::errmsg; unsigned int TestFixture::countTests; std::size_t TestFixture::fails_counter = 0; std::size_t TestFixture::todos_counter = 0; std::size_t TestFixture::succeeded_todos_counter = 0; std::set TestFixture::missingLibs; TestFixture::TestFixture(const char * const _name) :classname(_name) ,quiet_tests(false) { TestRegistry::theInstance().addTest(this); } bool TestFixture::prepareTest(const char testname[]) { // Check if tests should be executed if (testToRun.empty() || testToRun == testname) { // Tests will be executed - prepare them ++countTests; if (quiet_tests) { std::putchar('.'); // Use putchar to write through redirection of std::cout/cerr std::fflush(stdout); } else { std::cout << classname << "::" << testname << std::endl; } return true; } return false; } static std::string writestr(const std::string &str, bool gccStyle = false) { std::ostringstream ostr; if (gccStyle) ostr << '\"'; for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) { if (*i == '\n') { ostr << "\\n"; if ((i+1) != str.end() && !gccStyle) ostr << std::endl; } else if (*i == '\t') ostr << "\\t"; else if (*i == '\"') ostr << "\\\""; else ostr << *i; } if (!str.empty() && !gccStyle) ostr << std::endl; else if (gccStyle) ostr << '\"'; return ostr.str(); } void TestFixture::assert_(const char * const filename, const unsigned int linenr, const bool condition) const { if (!condition) { ++fails_counter; errmsg << filename << ':' << linenr << ": Assertion failed." << std::endl << "_____" << std::endl; } } void TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg) const { if (expected != actual) { ++fails_counter; errmsg << filename << ':' << linenr << ": Assertion failed. " << std::endl << "Expected: " << std::endl << writestr(expected) << std::endl << "Actual: " << std::endl << writestr(actual) << std::endl; if (!msg.empty()) errmsg << "Hint:" << std::endl << msg << std::endl; errmsg << "_____" << std::endl; } } std::string TestFixture::deleteLineNumber(const std::string &message) const { std::string result(message); // delete line number in "...:NUMBER:..." std::string::size_type pos = 0; std::string::size_type after = 0; while ((pos = result.find(':', pos)) != std::string::npos) { // get number if (pos + 1 == result.find_first_of("0123456789", pos + 1)) { if ((after = result.find_first_not_of("0123456789", pos + 1)) != std::string::npos && result.at(after) == ':') { // erase NUMBER result.erase(pos + 1, after - pos - 1); pos = after; } else { ++pos; } } else { ++pos; } } return result; } void TestFixture::assertEqualsWithoutLineNumbers(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg) const { assertEquals(filename, linenr, deleteLineNumber(expected), deleteLineNumber(actual), msg); } void TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const std::string& actual, const std::string &msg) const { assertEquals(filename, linenr, std::string(expected), actual, msg); } void TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const char actual[], const std::string &msg) const { assertEquals(filename, linenr, std::string(expected), std::string(actual), msg); } void TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const std::string& expected, const char actual[], const std::string &msg) const { assertEquals(filename, linenr, expected, std::string(actual), msg); } void TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const long long expected, const long long actual, const std::string &msg) const { if (expected != actual) { std::ostringstream ostr1; ostr1 << expected; std::ostringstream ostr2; ostr2 << actual; assertEquals(filename, linenr, ostr1.str(), ostr2.str(), msg); } } void TestFixture::assertEqualsDouble(const char * const filename, const unsigned int linenr, const double expected, const double actual, const double tolerance, const std::string &msg) const { if (expected < (actual - tolerance) || expected > (actual + tolerance)) { std::ostringstream ostr1; ostr1 << expected; std::ostringstream ostr2; ostr2 << actual; assertEquals(filename, linenr, ostr1.str(), ostr2.str(), msg); } } void TestFixture::todoAssertEquals(const char * const filename, const unsigned int linenr, const std::string &wanted, const std::string ¤t, const std::string &actual) const { if (wanted == actual) { errmsg << filename << ':' << linenr << ": Assertion succeeded unexpectedly. " << "Result: " << writestr(wanted, true) << std::endl << "_____" << std::endl; ++succeeded_todos_counter; } else { assertEquals(filename, linenr, current, actual); ++todos_counter; } } void TestFixture::todoAssertEquals(const char * const filename, const unsigned int linenr, const long long wanted, const long long current, const long long actual) const { std::ostringstream wantedStr, currentStr, actualStr; wantedStr << wanted; currentStr << current; actualStr << actual; todoAssertEquals(filename, linenr, wantedStr.str(), currentStr.str(), actualStr.str()); } void TestFixture::assertThrow(const char * const filename, const unsigned int linenr) const { ++fails_counter; errmsg << filename << ':' << linenr << ": Assertion succeeded. " << "The expected exception was thrown" << std::endl << "_____" << std::endl; } void TestFixture::assertThrowFail(const char * const filename, const unsigned int linenr) const { ++fails_counter; errmsg << filename << ':' << linenr << ": Assertion failed. " << "The expected exception was not thrown" << std::endl << "_____" << std::endl; } void TestFixture::assertNoThrowFail(const char * const filename, const unsigned int linenr) const { ++fails_counter; errmsg << filename << ':' << linenr << ": Assertion failed. " << "Unexpected exception was thrown" << std::endl << "_____" << std::endl; } void TestFixture::complainMissingLib(const char * const libname) const { missingLibs.insert(libname); } void TestFixture::run(const std::string &str) { testToRun = str; if (quiet_tests) { std::cout << '\n' << classname << ':'; } if (quiet_tests) { REDIRECT; run(); } else run(); } void TestFixture::processOptions(const options& args) { quiet_tests = args.quiet(); } std::size_t TestFixture::runTests(const options& args) { std::string classname(args.which_test()); std::string testname; if (classname.find("::") != std::string::npos) { testname = classname.substr(classname.find("::") + 2); classname.erase(classname.find("::")); } countTests = 0; errmsg.str(""); const std::list &tests = TestRegistry::theInstance().tests(); for (std::list::const_iterator it = tests.begin(); it != tests.end(); ++it) { if (classname.empty() || (*it)->classname == classname) { (*it)->processOptions(args); (*it)->run(testname); } } std::cout << "\n\nTesting Complete\nNumber of tests: " << countTests << std::endl; std::cout << "Number of todos: " << todos_counter; if (succeeded_todos_counter > 0) std::cout << " (" << succeeded_todos_counter << " succeeded)"; std::cout << std::endl; // calling flush here, to do all output before the error messages (in case the output is buffered) std::cout.flush(); std::cerr << "Tests failed: " << fails_counter << std::endl << std::endl; std::cerr << errmsg.str(); if (!missingLibs.empty()) { std::cerr << "Missing libraries: "; for (std::set::const_iterator i = missingLibs.begin(); i != missingLibs.end(); ++i) std::cerr << *i << " "; std::cerr << std::endl << std::endl; } std::cerr.flush(); return fails_counter; } void TestFixture::reportOut(const std::string & outmsg) { output << outmsg << std::endl; } void TestFixture::reportErr(const ErrorLogger::ErrorMessage &msg) { const std::string errormessage(msg.toString(false)); if (errout.str().find(errormessage) == std::string::npos) errout << errormessage << std::endl; } cppcheck-1.82/test/testsuite.h000066400000000000000000000130401322667425100164130ustar00rootroot00000000000000/* * 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 testsuiteH #define testsuiteH #include "config.h" #include "errorlogger.h" #include #include #include class options; class TestFixture : public ErrorLogger { private: static std::ostringstream errmsg; static unsigned int countTests; static std::size_t fails_counter; static std::size_t todos_counter; static std::size_t succeeded_todos_counter; static std::set missingLibs; protected: std::string classname; std::string testToRun; bool quiet_tests; virtual void run() = 0; bool prepareTest(const char testname[]); void assert_(const char * const filename, const unsigned int linenr, const bool condition) const; void assertEquals(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; void assertEqualsWithoutLineNumbers(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; void assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const std::string& actual, const std::string &msg = emptyString) const; void assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const char actual[], const std::string &msg = emptyString) const; void assertEquals(const char * const filename, const unsigned int linenr, const std::string& expected, const char actual[], const std::string &msg = emptyString) const; void assertEquals(const char * const filename, const unsigned int linenr, const long long expected, const long long actual, const std::string &msg = emptyString) const; void assertEqualsDouble(const char * const filename, const unsigned int linenr, const double expected, const double actual, const double tolerance, const std::string &msg = emptyString) const; void todoAssertEquals(const char * const filename, const unsigned int linenr, const std::string &wanted, const std::string ¤t, const std::string &actual) const; void todoAssertEquals(const char * const filename, const unsigned int linenr, const long long wanted, const long long current, const long long actual) const; void assertThrow(const char * const filename, const unsigned int linenr) const; void assertThrowFail(const char * const filename, const unsigned int linenr) const; void assertNoThrowFail(const char * const filename, const unsigned int linenr) const; void complainMissingLib(const char * const libname) const; std::string deleteLineNumber(const std::string &message) const; void processOptions(const options& args); public: virtual void reportOut(const std::string &outmsg); virtual void reportErr(const ErrorLogger::ErrorMessage &msg); void run(const std::string &str); explicit TestFixture(const char * const _name); virtual ~TestFixture() { } static std::size_t runTests(const options& args); }; extern std::ostringstream errout; extern std::ostringstream output; #define TEST_CASE( NAME ) if ( prepareTest(#NAME) ) { NAME(); } #define ASSERT( CONDITION ) assert_(__FILE__, __LINE__, CONDITION) #define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_EQUALS_WITHOUT_LINENUMBERS( EXPECTED , ACTUAL ) assertEqualsWithoutLineNumbers(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_EQUALS_DOUBLE( EXPECTED , ACTUAL, TOLERANCE ) assertEqualsDouble(__FILE__, __LINE__, EXPECTED, ACTUAL, TOLERANCE) #define ASSERT_EQUALS_MSG( EXPECTED , ACTUAL, MSG ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL, MSG) #define ASSERT_THROW( CMD, EXCEPTION ) try { CMD ; assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&) { } catch (...) { assertThrowFail(__FILE__, __LINE__); } #define ASSERT_NO_THROW( CMD ) try { CMD ; } catch (...) { assertNoThrowFail(__FILE__, __LINE__); } #define TODO_ASSERT_THROW( CMD, EXCEPTION ) try { CMD ; } catch (const EXCEPTION&) { } catch (...) { assertThrow(__FILE__, __LINE__); } #define TODO_ASSERT( CONDITION ) { bool condition=CONDITION; todoAssertEquals(__FILE__, __LINE__, true, false, condition); } #define TODO_ASSERT_EQUALS( WANTED , CURRENT , ACTUAL ) todoAssertEquals(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL) #define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance_##CLASSNAME; } #ifdef _WIN32 #define LOAD_LIB_2( LIB, NAME ) { if (((LIB).load("./testrunner", "../cfg/" NAME).errorcode != Library::OK) && ((LIB).load("./testrunner", "cfg/" NAME).errorcode != Library::OK)) { complainMissingLib(NAME); return; } } #else #define LOAD_LIB_2( LIB, NAME ) { if ((LIB).load("./testrunner", "cfg/" NAME).errorcode != Library::OK) { complainMissingLib(NAME); return; } } #endif #endif cppcheck-1.82/test/testsuppressions.cpp000066400000000000000000000426351322667425100204060ustar00rootroot00000000000000/* * 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 "config.h" #include "cppcheck.h" #include "settings.h" #include "suppressions.h" #include "testsuite.h" #include "threadexecutor.h" #include #include #include #include #include #include class TestSuppressions : public TestFixture { public: TestSuppressions() : TestFixture("TestSuppressions") { } private: void run() { TEST_CASE(suppressionsBadId1); TEST_CASE(suppressionsDosFormat); // Ticket #1836 TEST_CASE(suppressionsFileNameWithColon); // Ticket #1919 - filename includes colon TEST_CASE(suppressionsGlob); TEST_CASE(suppressionsFileNameWithExtraPath); TEST_CASE(suppressionsSettings); TEST_CASE(suppressionsMultiFile); TEST_CASE(suppressionsPathSeparator); TEST_CASE(inlinesuppress_unusedFunction); // #4210 - unusedFunction TEST_CASE(globalsuppress_unusedFunction); // #4946 TEST_CASE(suppressionWithRelativePaths); // #4733 TEST_CASE(suppressingSyntaxErrors); // #7076 TEST_CASE(suppressingSyntaxErrorsInline); // #5917 } void suppressionsBadId1() const { Suppressions suppressions; std::istringstream s1("123"); ASSERT_EQUALS("Failed to add suppression. Invalid id \"123\"", suppressions.parseFile(s1)); std::istringstream s2("obsoleteFunctionsrand_r"); ASSERT_EQUALS("", suppressions.parseFile(s2)); } void suppressionsDosFormat() const { Suppressions suppressions; std::istringstream s("abc\r\ndef\r\n"); ASSERT_EQUALS("", suppressions.parseFile(s)); ASSERT_EQUALS(true, suppressions.isSuppressed("abc", "test.cpp", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("def", "test.cpp", 1)); } void suppressionsFileNameWithColon() const { Suppressions suppressions; std::istringstream s("errorid:c:\\foo.cpp\nerrorid:c:\\bar.cpp:12"); ASSERT_EQUALS("", suppressions.parseFile(s)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:/foo.cpp", 1111)); ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "c:/bar.cpp", 10)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:/bar.cpp", 12)); } void suppressionsGlob() const { // Check for syntax errors in glob { Suppressions suppressions; std::istringstream s("errorid:**.cpp\n"); ASSERT_EQUALS("Failed to add suppression. Syntax error in glob.", suppressions.parseFile(s)); } // Check that globbing works { Suppressions suppressions; std::istringstream s("errorid:x*.cpp\nerrorid:y?.cpp\nerrorid:test.c*"); ASSERT_EQUALS("", suppressions.parseFile(s)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp.cpp", 1)); ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "abc.cpp", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "ya.cpp", 1)); ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "y.cpp", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.c", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.cpp", 1)); } // Check that both a filename match and a glob match apply { Suppressions suppressions; std::istringstream s("errorid:x*.cpp\nerrorid:xyz.cpp:1\nerrorid:a*.cpp:1\nerrorid:abc.cpp:2"); ASSERT_EQUALS("", suppressions.parseFile(s)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 2)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 1)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 2)); } } void suppressionsFileNameWithExtraPath() const { // Ticket #2797 Suppressions suppressions; suppressions.addSuppression("errorid", "./a.c", 123); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "a.c", 123)); } void reportSuppressions(const Settings &settings, const std::map &files) { // make it verbose that this check is disabled const bool unusedFunctionCheck = false; if (settings.jointSuppressionReport) { for (std::map::const_iterator i = files.begin(); i != files.end(); ++i) { reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(i->first, unusedFunctionCheck)); } } reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions(unusedFunctionCheck)); } // Check the suppression unsigned int checkSuppression(const char code[], const std::string &suppression = emptyString) { std::map files; files["test.cpp"] = code; return checkSuppression(files, suppression); } // Check the suppression for multiple files unsigned int checkSuppression(std::map &files, const std::string &suppression = emptyString) { // Clear the error log errout.str(""); CppCheck cppCheck(*this, true); Settings& settings = cppCheck.settings(); settings.inlineSuppressions = true; settings.addEnabled("information"); settings.jointSuppressionReport = true; if (!suppression.empty()) { std::string r = settings.nomsg.addSuppressionLine(suppression); ASSERT_EQUALS("", r); } unsigned int exitCode = 0; for (std::map::const_iterator file = files.begin(); file != files.end(); ++file) { exitCode |= cppCheck.check(file->first, file->second); } if (cppCheck.analyseWholeProgram()) exitCode |= settings.exitCode; reportSuppressions(settings, files); return exitCode; } unsigned int checkSuppressionThreads(const char code[], const std::string &suppression = emptyString) { errout.str(""); output.str(""); std::map files; files["test.cpp"] = 1; Settings settings; settings.jobs = 1; settings.inlineSuppressions = true; settings.addEnabled("information"); if (!suppression.empty()) { ASSERT_EQUALS("", settings.nomsg.addSuppressionLine(suppression)); } ThreadExecutor executor(files, settings, *this); for (std::map::const_iterator i = files.begin(); i != files.end(); ++i) executor.addFileContent(i->first, code); unsigned int exitCode = executor.check(); std::map files_for_report; for (std::map::const_iterator file = files.begin(); file != files.end(); ++file) files_for_report[file->first] = ""; reportSuppressions(settings, files_for_report); return exitCode; } void runChecks(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { // check to make sure the appropriate error is present (this->*check)("void f() {\n" " int a;\n" " a++;\n" "}\n", ""); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); // suppress uninitvar globally (this->*check)("void f() {\n" " int a;\n" " a++;\n" "}\n", "uninitvar"); ASSERT_EQUALS("", errout.str()); // suppress uninitvar globally, without error present (this->*check)("void f() {\n" " int a;\n" " b++;\n" "}\n", "uninitvar"); ASSERT_EQUALS("[*]: (information) Unmatched suppression: uninitvar\n", errout.str()); // suppress uninitvar for this file only (this->*check)("void f() {\n" " int a;\n" " a++;\n" "}\n", "uninitvar:test.cpp"); ASSERT_EQUALS("", errout.str()); // suppress uninitvar for this file only, without error present (this->*check)("void f() {\n" " int a;\n" " b++;\n" "}\n", "uninitvar:test.cpp"); ASSERT_EQUALS("[test.cpp]: (information) Unmatched suppression: uninitvar\n", errout.str()); // suppress all for this file only (this->*check)("void f() {\n" " int a;\n" " a++;\n" "}\n", "*:test.cpp"); ASSERT_EQUALS("", errout.str()); // suppress all for this file only, without error present (this->*check)("void f() {\n" " int a;\n" " b++;\n" "}\n", "*:test.cpp"); ASSERT_EQUALS("[test.cpp]: (information) Unmatched suppression: *\n", errout.str()); // suppress uninitvar for this file and line (this->*check)("void f() {\n" " int a;\n" " a++;\n" "}\n", "uninitvar:test.cpp:3"); ASSERT_EQUALS("", errout.str()); // suppress uninitvar for this file and line, without error present (this->*check)("void f() {\n" " int a;\n" " b++;\n" "}\n", "uninitvar:test.cpp:3"); ASSERT_EQUALS("[test.cpp:3]: (information) Unmatched suppression: uninitvar\n", errout.str()); // suppress uninitvar inline (this->*check)("void f() {\n" " int a;\n" " // cppcheck-suppress uninitvar\n" " a++;\n" "}\n", ""); ASSERT_EQUALS("", errout.str()); // suppress uninitvar inline (this->*check)("void f() {\n" " int a;\n" " // cppcheck-suppress uninitvar\n" "\n" " a++;\n" "}\n", ""); ASSERT_EQUALS("", errout.str()); // suppress uninitvar inline (this->*check)("void f() {\n" " int a;\n" " /* cppcheck-suppress uninitvar */\n" " a++;\n" "}\n", ""); ASSERT_EQUALS("", errout.str()); // suppress uninitvar inline (this->*check)("void f() {\n" " int a;\n" " /* cppcheck-suppress uninitvar */\n" "\n" " a++;\n" "}\n", ""); ASSERT_EQUALS("", errout.str()); // suppress uninitvar inline, with asm before (#6813) (this->*check)("void f() {\n" " __asm {\n" " foo\n" " }" " int a;\n" " // cppcheck-suppress uninitvar\n" " a++;\n" "}", ""); ASSERT_EQUALS("", errout.str()); // suppress uninitvar inline, without error present (this->*check)("void f() {\n" " int a;\n" " // cppcheck-suppress uninitvar\n" " b++;\n" "}\n", ""); ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str()); // #5746 - exitcode ASSERT_EQUALS(1U, (this->*check)("int f() {\n" " int a; return a;\n" "}\n", "")); ASSERT_EQUALS(0U, (this->*check)("int f() {\n" " int a; return a;\n" "}\n", "uninitvar")); } void suppressionsSettings() { runChecks(&TestSuppressions::checkSuppression); if (ThreadExecutor::isEnabled()) runChecks(&TestSuppressions::checkSuppressionThreads); } void suppressionsMultiFile() { std::map files; files["abc.cpp"] = "void f() {\n" "}\n"; files["xyz.cpp"] = "void f() {\n" " int a;\n" " a++;\n" "}\n"; // suppress uninitvar for this file and line checkSuppression(files, "uninitvar:xyz.cpp:3"); ASSERT_EQUALS("", errout.str()); } void suppressionsPathSeparator() const { Suppressions suppressions; suppressions.addSuppressionLine("*:test\\*"); ASSERT_EQUALS(true, suppressions.isSuppressed("someid", "test/foo/bar.cpp", 142)); suppressions.addSuppressionLine("abc:include/1.h"); ASSERT_EQUALS(true, suppressions.isSuppressed("abc", "include\\1.h", 142)); } void inlinesuppress_unusedFunction() const { // #4210, #4946 - wrong report of "unmatchedSuppression" for "unusedFunction" Suppressions suppressions; suppressions.addSuppression("unusedFunction", "test.c", 3U); ASSERT_EQUALS(true, !suppressions.getUnmatchedLocalSuppressions("test.c", true).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedGlobalSuppressions(true).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", false).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedGlobalSuppressions(false).empty()); } void globalsuppress_unusedFunction() const { // #4946 - wrong report of "unmatchedSuppression" for "unusedFunction" Suppressions suppressions; suppressions.addSuppressionLine("unusedFunction:*"); ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", true).empty()); ASSERT_EQUALS(true, !suppressions.getUnmatchedGlobalSuppressions(true).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", false).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedGlobalSuppressions(false).empty()); } void suppressionWithRelativePaths() { // Clear the error log errout.str(""); CppCheck cppCheck(*this, true); Settings& settings = cppCheck.settings(); settings.addEnabled("style"); settings.inlineSuppressions = true; settings.relativePaths = true; settings.basePaths.push_back("/somewhere"); const char code[] = "struct Point\n" "{\n" " // cppcheck-suppress unusedStructMember\n" " int x;\n" " // cppcheck-suppress unusedStructMember\n" " int y;\n" "};"; cppCheck.check("/somewhere/test.cpp", code); ASSERT_EQUALS("",errout.str()); } void suppressingSyntaxErrors() { // syntaxErrors should be suppressable (#7076) std::map files; files["test.cpp"] = "if if\n"; checkSuppression(files, "syntaxError:test.cpp:1"); ASSERT_EQUALS("", errout.str()); } void suppressingSyntaxErrorsInline() { // syntaxErrors should be suppressable (#5917) std::map files; files["test.cpp"] = "double result(0.0);\n" "_asm\n" "{\n" " // cppcheck-suppress syntaxError\n" " push EAX ; save EAX for callers \n" " mov EAX,Real10 ; get the address pointed to by Real10\n" " fld TBYTE PTR [EAX] ; load an extended real (10 bytes)\n" " fstp QWORD PTR result ; store a double (8 bytes)\n" " pop EAX ; restore EAX\n" "}"; checkSuppression(files, ""); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestSuppressions) cppcheck-1.82/test/testsymboldatabase.cpp000066400000000000000000007470711322667425100206310ustar00rootroot00000000000000/* * 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 "library.h" #include "platform.h" #include "settings.h" #include "symboldatabase.h" #include "testsuite.h" #include "testutils.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include #include #include struct InternalError; #define GET_SYMBOL_DB(code) \ Tokenizer tokenizer(&settings1, this); \ const SymbolDatabase *db = getSymbolDB_inner(tokenizer, code, "test.cpp"); #define GET_SYMBOL_DB_C(code) \ Tokenizer tokenizer(&settings1, this); \ const SymbolDatabase *db = getSymbolDB_inner(tokenizer, code, "test.c"); class TestSymbolDatabase: public TestFixture { public: TestSymbolDatabase() :TestFixture("TestSymbolDatabase") ,si(nullptr, nullptr, nullptr) ,vartok(nullptr) ,typetok(nullptr) ,t(nullptr) ,found(false) { } private: const Scope si; const Token* vartok; const Token* typetok; const Token* t; bool found; Settings settings1; Settings settings2; void reset() { vartok = nullptr; typetok = nullptr; t = nullptr; found = false; } const static SymbolDatabase* getSymbolDB_inner(Tokenizer& tokenizer, const char* code, const char* filename) { errout.str(""); std::istringstream istr(code); tokenizer.tokenize(istr, filename); return tokenizer.getSymbolDatabase(); } static const Scope *findFunctionScopeByToken(const SymbolDatabase * db, const Token *tok) { std::list::const_iterator scope; for (scope = db->scopeList.begin(); scope != db->scopeList.end(); ++scope) { if (scope->type == Scope::eFunction) { if (scope->classDef == tok) return &(*scope); } } return 0; } static const Function *findFunctionByName(const char str[], const Scope* startScope) { const Scope* currScope = startScope; while (currScope && currScope->isExecutable()) { if (currScope->functionOf) currScope = currScope->functionOf; else currScope = currScope->nestedIn; } while (currScope) { for (std::list::const_iterator i = currScope->functionList.begin(); i != currScope->functionList.end(); ++i) { if (i->tokenDef->str() == str) return &*i; } currScope = currScope->nestedIn; } return 0; } void run() { LOAD_LIB_2(settings1.library, "std.cfg"); settings2.platform(Settings::Unspecified); TEST_CASE(array); TEST_CASE(stlarray); TEST_CASE(test_isVariableDeclarationCanHandleNull); TEST_CASE(test_isVariableDeclarationIdentifiesSimpleDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesInitialization); TEST_CASE(test_isVariableDeclarationIdentifiesCpp11Initialization); TEST_CASE(test_isVariableDeclarationIdentifiesScopedDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesStdDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesScopedStdDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesManyScopes); TEST_CASE(test_isVariableDeclarationIdentifiesPointers); TEST_CASE(test_isVariableDeclarationDoesNotIdentifyConstness); TEST_CASE(test_isVariableDeclarationIdentifiesFirstOfManyVariables); TEST_CASE(test_isVariableDeclarationIdentifiesScopedPointerDeclaration); TEST_CASE(test_isVariableDeclarationIdentifiesDeclarationWithIndirection); TEST_CASE(test_isVariableDeclarationIdentifiesDeclarationWithMultipleIndirection); TEST_CASE(test_isVariableDeclarationIdentifiesArray); TEST_CASE(test_isVariableDeclarationIdentifiesPointerArray); TEST_CASE(test_isVariableDeclarationIdentifiesOfArrayPointers); TEST_CASE(isVariableDeclarationIdentifiesTemplatedPointerVariable); TEST_CASE(isVariableDeclarationIdentifiesTemplatedPointerToPointerVariable); TEST_CASE(isVariableDeclarationIdentifiesTemplatedArrayVariable); TEST_CASE(isVariableDeclarationIdentifiesTemplatedVariable); TEST_CASE(isVariableDeclarationIdentifiesTemplatedVariableIterator); TEST_CASE(isVariableDeclarationIdentifiesNestedTemplateVariable); TEST_CASE(isVariableDeclarationIdentifiesReference); TEST_CASE(isVariableDeclarationDoesNotIdentifyTemplateClass); TEST_CASE(isVariableDeclarationDoesNotIdentifyCppCast); TEST_CASE(isVariableDeclarationPointerConst); TEST_CASE(isVariableDeclarationRValueRef); TEST_CASE(isVariableStlType); TEST_CASE(rangeBasedFor); TEST_CASE(arrayMemberVar1); TEST_CASE(arrayMemberVar2); TEST_CASE(arrayMemberVar3); TEST_CASE(staticMemberVar); TEST_CASE(getVariableFromVarIdBoundsCheck); TEST_CASE(hasRegularFunction); TEST_CASE(hasRegularFunction_trailingReturnType); TEST_CASE(hasInlineClassFunction); TEST_CASE(hasInlineClassFunction_trailingReturnType); TEST_CASE(hasMissingInlineClassFunction); TEST_CASE(hasClassFunction); TEST_CASE(hasClassFunction_trailingReturnType); TEST_CASE(hasRegularFunctionReturningFunctionPointer); TEST_CASE(hasInlineClassFunctionReturningFunctionPointer); TEST_CASE(hasMissingInlineClassFunctionReturningFunctionPointer); TEST_CASE(hasInlineClassOperatorTemplate); TEST_CASE(hasClassFunctionReturningFunctionPointer); TEST_CASE(methodWithRedundantScope); TEST_CASE(complexFunctionArrayPtr); TEST_CASE(pointerToMemberFunction); TEST_CASE(hasSubClassConstructor); TEST_CASE(testConstructors); TEST_CASE(functionDeclarationTemplate); TEST_CASE(functionDeclarations); TEST_CASE(constructorInitialization); TEST_CASE(memberFunctionOfUnknownClassMacro1); TEST_CASE(memberFunctionOfUnknownClassMacro2); TEST_CASE(memberFunctionOfUnknownClassMacro3); TEST_CASE(functionLinkage); TEST_CASE(classWithFriend); TEST_CASE(parseFunctionCorrect); TEST_CASE(parseFunctionDeclarationCorrect); TEST_CASE(Cpp11InitInInitList); TEST_CASE(hasGlobalVariables1); TEST_CASE(hasGlobalVariables2); TEST_CASE(hasGlobalVariables3); TEST_CASE(checkTypeStartEndToken1); TEST_CASE(checkTypeStartEndToken2); // handling for unknown macro: 'void f() MACRO {..' TEST_CASE(checkTypeStartEndToken3); // no variable name: void f(const char){} TEST_CASE(functionArgs1); TEST_CASE(functionArgs2); TEST_CASE(functionArgs3); TEST_CASE(functionArgs4); TEST_CASE(functionArgs5); // #7650 TEST_CASE(functionArgs6); // #7651 TEST_CASE(functionArgs7); // #7652 TEST_CASE(functionArgs8); // #7653 TEST_CASE(functionArgs9); // #7657 TEST_CASE(functionArgs10); TEST_CASE(functionArgs11); TEST_CASE(functionArgs12); // #7661 TEST_CASE(functionArgs13); // #7697 TEST_CASE(namespaces1); TEST_CASE(namespaces2); TEST_CASE(namespaces3); // #3854 - unknown macro TEST_CASE(namespaces4); TEST_CASE(tryCatch1); TEST_CASE(symboldatabase1); TEST_CASE(symboldatabase2); TEST_CASE(symboldatabase3); // ticket #2000 TEST_CASE(symboldatabase4); TEST_CASE(symboldatabase5); // ticket #2178 TEST_CASE(symboldatabase6); // ticket #2221 TEST_CASE(symboldatabase7); // ticket #2230 TEST_CASE(symboldatabase8); // ticket #2252 TEST_CASE(symboldatabase9); // ticket #2525 TEST_CASE(symboldatabase10); // ticket #2537 TEST_CASE(symboldatabase11); // ticket #2539 TEST_CASE(symboldatabase12); // ticket #2547 TEST_CASE(symboldatabase13); // ticket #2577 TEST_CASE(symboldatabase14); // ticket #2589 TEST_CASE(symboldatabase17); // ticket #2657 TEST_CASE(symboldatabase19); // ticket #2991 (segmentation fault) TEST_CASE(symboldatabase20); // ticket #3013 (segmentation fault) TEST_CASE(symboldatabase21); TEST_CASE(symboldatabase22); // ticket #3437 (segmentation fault) TEST_CASE(symboldatabase23); // ticket #3435 TEST_CASE(symboldatabase24); // ticket #3508 (constructor, destructor) TEST_CASE(symboldatabase25); // ticket #3561 (throw C++) TEST_CASE(symboldatabase26); // ticket #3561 (throw C) TEST_CASE(symboldatabase27); // ticket #3543 (segmentation fault) TEST_CASE(symboldatabase28); TEST_CASE(symboldatabase29); // ticket #4442 (segmentation fault) TEST_CASE(symboldatabase30); TEST_CASE(symboldatabase31); TEST_CASE(symboldatabase32); TEST_CASE(symboldatabase33); // ticket #4682 (false negatives) TEST_CASE(symboldatabase34); // ticket #4694 (segmentation fault) TEST_CASE(symboldatabase35); // ticket #4806 (segmentation fault) TEST_CASE(symboldatabase36); // ticket #4892 (segmentation fault) TEST_CASE(symboldatabase37); TEST_CASE(symboldatabase38); // ticket #5125 (infinite recursion) TEST_CASE(symboldatabase40); // ticket #5153 TEST_CASE(symboldatabase41); // ticket #5197 (unknown macro) TEST_CASE(symboldatabase42); // only put variables in variable list TEST_CASE(symboldatabase43); // #4738 TEST_CASE(symboldatabase44); TEST_CASE(symboldatabase45); // #6125 TEST_CASE(symboldatabase46); // #6171 (anonymous namespace) TEST_CASE(symboldatabase47); // #6308 TEST_CASE(symboldatabase48); // #6417 TEST_CASE(symboldatabase49); // #6424 TEST_CASE(symboldatabase50); // #6432 TEST_CASE(symboldatabase51); // #6538 TEST_CASE(symboldatabase52); // #6581 TEST_CASE(symboldatabase53); // #7124 (library podtype) TEST_CASE(symboldatabase54); // #7257 TEST_CASE(symboldatabase55); // #7767 (return unknown macro) TEST_CASE(symboldatabase56); // #7909 TEST_CASE(symboldatabase57); TEST_CASE(symboldatabase58); // #6985 (using namespace type lookup) TEST_CASE(enum1); TEST_CASE(enum2); TEST_CASE(enum3); TEST_CASE(enum4); TEST_CASE(enum5); TEST_CASE(enum6); TEST_CASE(enum7); TEST_CASE(sizeOfType); TEST_CASE(isImplicitlyVirtual); TEST_CASE(isPure); TEST_CASE(isFunction); // UNKNOWN_MACRO(a,b) { .. } TEST_CASE(findFunction1); TEST_CASE(findFunction2); // mismatch: parameter passed by address => reference argument TEST_CASE(findFunction3); TEST_CASE(findFunction4); TEST_CASE(findFunction5); // #6230 TEST_CASE(findFunction6); TEST_CASE(findFunction7); // #6700 TEST_CASE(findFunction8); TEST_CASE(findFunction9); TEST_CASE(findFunction10); // #7673 TEST_CASE(findFunction11); TEST_CASE(findFunction12); TEST_CASE(findFunction13); TEST_CASE(findFunction14); TEST_CASE(findFunction15); TEST_CASE(findFunction16); TEST_CASE(findFunction17); TEST_CASE(findFunction18); TEST_CASE(noexceptFunction1); TEST_CASE(noexceptFunction2); TEST_CASE(noexceptFunction3); TEST_CASE(noexceptFunction4); TEST_CASE(throwFunction1); TEST_CASE(throwFunction2); TEST_CASE(nothrowAttributeFunction); TEST_CASE(nothrowDeclspecFunction); TEST_CASE(varTypesIntegral); // known integral TEST_CASE(varTypesFloating); // known floating TEST_CASE(varTypesOther); // (un)known TEST_CASE(functionPrototype); // #5867 TEST_CASE(lambda); // #5867 TEST_CASE(lambda2); // #7473 TEST_CASE(circularDependencies); // #6298 TEST_CASE(executableScopeWithUnknownFunction); TEST_CASE(valuetype); TEST_CASE(variadic1); // #7453 TEST_CASE(variadic2); // #7649 TEST_CASE(variadic3); // #7387 TEST_CASE(noReturnType); TEST_CASE(auto1); TEST_CASE(auto2); TEST_CASE(auto3); TEST_CASE(auto4); TEST_CASE(auto5); TEST_CASE(auto6); // #7963 (segmentation fault) TEST_CASE(auto7); TEST_CASE(auto8); TEST_CASE(auto9); // #8044 (segmentation fault) TEST_CASE(auto10); // #8020 TEST_CASE(unionWithConstructor); TEST_CASE(using1); TEST_CASE(using2); // #8331 } void array() { GET_SYMBOL_DB_C("int a[10+2];"); ASSERT(db != nullptr); if (!db) return; ASSERT(db->getVariableListSize() == 2); // the first one is not used const Variable * v = db->getVariableFromVarId(1); ASSERT(v != nullptr); if (!v) return; ASSERT(v->isArray()); ASSERT_EQUALS(1U, v->dimensions().size()); ASSERT_EQUALS(12U, v->dimension(0)); } void stlarray() const { std::istringstream code("std::array arr;"); TokenList list(nullptr); list.createTokens(code, "test.c"); list.front()->tokAt(3)->link(list.front()->tokAt(7)); Variable v(list.front()->next(), list.front(), list.back(), 0, Public, nullptr, nullptr, &settings1.library); ASSERT(v.isArray()); ASSERT_EQUALS(1U, v.dimensions().size()); ASSERT_EQUALS(20U, v.dimension(0)); } void test_isVariableDeclarationCanHandleNull() { reset(); bool result = si.isVariableDeclaration(nullptr, vartok, typetok); ASSERT_EQUALS(false, result); ASSERT(nullptr == vartok); ASSERT(nullptr == typetok); Variable v(nullptr, nullptr, nullptr, 0, Public, 0, 0, &settings1.library); } void test_isVariableDeclarationIdentifiesSimpleDeclaration() { reset(); givenACodeSampleToTokenize simpleDeclaration("int x;"); bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesInitialization() { reset(); givenACodeSampleToTokenize simpleDeclaration("int x (1);"); bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesCpp11Initialization() { reset(); givenACodeSampleToTokenize simpleDeclaration("int x {1};"); bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesScopedDeclaration() { reset(); givenACodeSampleToTokenize ScopedDeclaration("::int x;"); bool result = si.isVariableDeclaration(ScopedDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesStdDeclaration() { reset(); givenACodeSampleToTokenize StdDeclaration("std::string x;"); bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("string", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesScopedStdDeclaration() { reset(); givenACodeSampleToTokenize StdDeclaration("::std::string x;"); bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("string", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesManyScopes() { reset(); givenACodeSampleToTokenize manyScopes("AA::BB::CC::DD::EE x;"); bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("x", vartok->str()); ASSERT_EQUALS("EE", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesPointers() { reset(); givenACodeSampleToTokenize pointer("int* p;"); bool result1 = si.isVariableDeclaration(pointer.tokens(), vartok, typetok); ASSERT_EQUALS(true, result1); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v1(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v1.isArray()); ASSERT(true == v1.isPointer()); ASSERT(false == v1.isReference()); reset(); givenACodeSampleToTokenize constpointer("const int* p;"); Variable v2(constpointer.tokens()->tokAt(3), constpointer.tokens()->next(), constpointer.tokens()->tokAt(2), 0, Public, 0, 0, &settings1.library); ASSERT(false == v2.isArray()); ASSERT(true == v2.isPointer()); ASSERT(false == v2.isConst()); ASSERT(false == v2.isReference()); reset(); givenACodeSampleToTokenize pointerconst("int* const p;"); bool result2 = si.isVariableDeclaration(pointerconst.tokens(), vartok, typetok); ASSERT_EQUALS(true, result2); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v3(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v3.isArray()); ASSERT(true == v3.isPointer()); ASSERT(true == v3.isConst()); ASSERT(false == v3.isReference()); } void test_isVariableDeclarationDoesNotIdentifyConstness() { reset(); givenACodeSampleToTokenize constness("const int* cp;"); bool result = si.isVariableDeclaration(constness.tokens(), vartok, typetok); ASSERT_EQUALS(false, result); ASSERT(nullptr == vartok); ASSERT(nullptr == typetok); } void test_isVariableDeclarationIdentifiesFirstOfManyVariables() { reset(); givenACodeSampleToTokenize multipleDeclaration("int first, second;"); bool result = si.isVariableDeclaration(multipleDeclaration.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("first", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesScopedPointerDeclaration() { reset(); givenACodeSampleToTokenize manyScopes("AA::BB::CC::DD::EE* p;"); bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("EE", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(true == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesDeclarationWithIndirection() { reset(); givenACodeSampleToTokenize pointerToPointer("int** pp;"); bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("pp", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(true == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesDeclarationWithMultipleIndirection() { reset(); givenACodeSampleToTokenize pointerToPointer("int***** p;"); bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("p", vartok->str()); ASSERT_EQUALS("int", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(true == v.isPointer()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesArray() { reset(); givenACodeSampleToTokenize arr("::std::string v[3];"); bool result = si.isVariableDeclaration(arr.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("v", vartok->str()); ASSERT_EQUALS("string", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(true == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isPointerArray()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesPointerArray() { reset(); givenACodeSampleToTokenize arr("A *a[5];"); bool result = si.isVariableDeclaration(arr.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("a", vartok->str()); ASSERT_EQUALS("A", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isPointer()); ASSERT(true == v.isArray()); ASSERT(false == v.isPointerToArray()); ASSERT(true == v.isPointerArray()); ASSERT(false == v.isReference()); } void test_isVariableDeclarationIdentifiesOfArrayPointers() { reset(); givenACodeSampleToTokenize arr("A (*a)[5];"); bool result = si.isVariableDeclaration(arr.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("a", vartok->str()); ASSERT_EQUALS("A", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(true == v.isPointer()); ASSERT(false == v.isArray()); ASSERT(true == v.isPointerToArray()); ASSERT(false == v.isPointerArray()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesTemplatedPointerVariable() { reset(); givenACodeSampleToTokenize var("std::set* chars;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("chars", vartok->str()); ASSERT_EQUALS("set", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(true == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesTemplatedPointerToPointerVariable() { reset(); givenACodeSampleToTokenize var("std::deque*** ints;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("ints", vartok->str()); ASSERT_EQUALS("deque", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(true == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesTemplatedArrayVariable() { reset(); givenACodeSampleToTokenize var("std::deque ints[3];"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("ints", vartok->str()); ASSERT_EQUALS("deque", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(true == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesTemplatedVariable() { reset(); givenACodeSampleToTokenize var("std::vector ints;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("ints", vartok->str()); ASSERT_EQUALS("vector", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesTemplatedVariableIterator() { reset(); givenACodeSampleToTokenize var("std::list::const_iterator floats;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("floats", vartok->str()); ASSERT_EQUALS("const_iterator", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesNestedTemplateVariable() { reset(); givenACodeSampleToTokenize var("std::deque > intsets;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); ASSERT_EQUALS("intsets", vartok->str()); ASSERT_EQUALS("deque", typetok->str()); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationIdentifiesReference() { reset(); givenACodeSampleToTokenize var1("int& foo;"); bool result1 = si.isVariableDeclaration(var1.tokens(), vartok, typetok); ASSERT_EQUALS(true, result1); Variable v1(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v1.isArray()); ASSERT(false == v1.isPointer()); ASSERT(true == v1.isReference()); reset(); givenACodeSampleToTokenize var2("foo*& bar;"); bool result2 = si.isVariableDeclaration(var2.tokens(), vartok, typetok); ASSERT_EQUALS(true, result2); Variable v2(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v2.isArray()); ASSERT(true == v2.isPointer()); ASSERT(true == v2.isReference()); reset(); givenACodeSampleToTokenize var3("std::vector& foo;"); bool result3 = si.isVariableDeclaration(var3.tokens(), vartok, typetok); ASSERT_EQUALS(true, result3); Variable v3(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v3.isArray()); ASSERT(false == v3.isPointer()); ASSERT(true == v3.isReference()); } void isVariableDeclarationDoesNotIdentifyTemplateClass() { reset(); givenACodeSampleToTokenize var("template class SomeClass{};"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(false, result); } void isVariableDeclarationDoesNotIdentifyCppCast() { reset(); givenACodeSampleToTokenize var("reinterpret_cast (code)[0] = 0;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(false, result); } void isVariableDeclarationPointerConst() { reset(); givenACodeSampleToTokenize var("std::string const* s;"); bool result = si.isVariableDeclaration(var.tokens()->next(), vartok, typetok); ASSERT_EQUALS(true, result); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(true == v.isPointer()); ASSERT(false == v.isReference()); } void isVariableDeclarationRValueRef() { reset(); givenACodeSampleToTokenize var("int&& i;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1.library); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(true == v.isReference()); ASSERT(true == v.isRValueReference()); ASSERT(var.tokens()->tokAt(2)->scope() != 0); } void rangeBasedFor() { GET_SYMBOL_DB("void reset() {\n" " for(auto& e : array)\n" " foo(e);\n" "}"); ASSERT(db != nullptr); if (!db) return; ASSERT(db->scopeList.back().type == Scope::eFor); ASSERT_EQUALS(2, db->getVariableListSize()); if (db->getVariableListSize() < 2) return; const Variable* e = db->getVariableFromVarId(1); ASSERT(e && e->isReference() && e->isLocal()); } void isVariableStlType() { { reset(); std::istringstream code("std::string s;"); TokenList list(nullptr); list.createTokens(code, "test.cpp"); bool result = si.isVariableDeclaration(list.front(), vartok, typetok); ASSERT_EQUALS(true, result); Variable v(vartok, list.front(), list.back(), 0, Public, 0, 0, &settings1.library); static const std::set types = make_container< std::set >() << "string" << "wstring" ; static const std::set no_types = make_container< std::set >() << "set" ; ASSERT_EQUALS(true, v.isStlType()); ASSERT_EQUALS(true, v.isStlType(types)); ASSERT_EQUALS(false, v.isStlType(no_types)); ASSERT_EQUALS(true, v.isStlStringType()); } { reset(); std::istringstream code("std::vector v;"); TokenList list(nullptr); list.createTokens(code, "test.cpp"); list.front()->tokAt(3)->link(list.front()->tokAt(5)); bool result = si.isVariableDeclaration(list.front(), vartok, typetok); ASSERT_EQUALS(true, result); Variable v(vartok, list.front(), list.back(), 0, Public, 0, 0, &settings1.library); static const std::set types = make_container< std::set >() << "bitset" << "set" << "vector" << "wstring" ; static const std::set no_types = make_container< std::set >() << "bitset" << "map" << "set" ; ASSERT_EQUALS(true, v.isStlType()); ASSERT_EQUALS(true, v.isStlType(types)); ASSERT_EQUALS(false, v.isStlType(no_types)); ASSERT_EQUALS(false, v.isStlStringType()); } { reset(); std::istringstream code("SomeClass s;"); TokenList list(nullptr); list.createTokens(code, "test.cpp"); bool result = si.isVariableDeclaration(list.front(), vartok, typetok); ASSERT_EQUALS(true, result); Variable v(vartok, list.front(), list.back(), 0, Public, 0, 0, &settings1.library); static const std::set types = make_container< std::set >() << "bitset" << "set" << "vector" ; ASSERT_EQUALS(false, v.isStlType()); ASSERT_EQUALS(false, v.isStlType(types)); ASSERT_EQUALS(false, v.isStlStringType()); } } void arrayMemberVar1() { GET_SYMBOL_DB("struct Foo {\n" " int x;\n" "};\n" "void f() {\n" " struct Foo foo[10];\n" " foo[1].x = 123;\n" // <- x should get a variable() pointer "}"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), ". x"); tok = tok ? tok->next() : nullptr; ASSERT(db != nullptr); ASSERT(tok && tok->variable() && Token::simpleMatch(tok->variable()->typeStartToken(), "int x ;")); ASSERT(tok && tok->varId() == 3U); // It's possible to set a varId } void arrayMemberVar2() { GET_SYMBOL_DB("struct Foo {\n" " int x;\n" "};\n" "void f() {\n" " struct Foo foo[10][10];\n" " foo[1][2].x = 123;\n" // <- x should get a variable() pointer "}"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), ". x"); tok = tok ? tok->next() : nullptr; ASSERT(db != nullptr); ASSERT(tok && tok->variable() && Token::simpleMatch(tok->variable()->typeStartToken(), "int x ;")); ASSERT(tok && tok->varId() == 3U); // It's possible to set a varId } void arrayMemberVar3() { GET_SYMBOL_DB("struct Foo {\n" " int x;\n" "};\n" "void f() {\n" " struct Foo foo[10];\n" " (foo[1]).x = 123;\n" // <- x should get a variable() pointer "}"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), ". x"); tok = tok ? tok->next() : nullptr; ASSERT(db != nullptr); ASSERT(tok && tok->variable() && Token::simpleMatch(tok->variable()->typeStartToken(), "int x ;")); ASSERT(tok && tok->varId() == 3U); // It's possible to set a varId } void staticMemberVar() { GET_SYMBOL_DB("class Foo {\n" " static const double d;\n" "};\n" "const double Foo::d = 5.0;"); const Variable* v = db->getVariableFromVarId(1); ASSERT(v && db->getVariableListSize() == 2); ASSERT(v && v->isStatic() && v->isConst() && v->isPrivate()); } void getVariableFromVarIdBoundsCheck() { GET_SYMBOL_DB("int x;\n" "int y;\n"); const Variable* v = db->getVariableFromVarId(2); // three elements: varId 0 also counts via a fake-entry ASSERT(v && db->getVariableListSize() == 3); ASSERT_THROW(db->getVariableFromVarId(3), std::out_of_range); } void hasRegularFunction() { GET_SYMBOL_DB("void func() { }\n") // 2 scopes: Global and Function ASSERT(db && db->scopeList.size() == 2); if (db) { const Scope *scope = findFunctionScopeByToken(db, tokenizer.tokens()->next()); ASSERT(scope && scope->className == "func"); if (!scope) return; ASSERT(scope->functionOf == 0); const Function *function = findFunctionByName("func", &db->scopeList.front()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == tokenizer.tokens()->next()); ASSERT(function && function->hasBody()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn != scope); ASSERT(function && function->retDef == tokenizer.tokens()); } } void hasRegularFunction_trailingReturnType() { GET_SYMBOL_DB("auto func() -> int { }") // 2 scopes: Global and Function ASSERT(db && db->scopeList.size() == 2); if (db) { const Scope *scope = findFunctionScopeByToken(db, tokenizer.tokens()->next()); ASSERT(scope && scope->className == "func"); if (!scope) return; ASSERT(scope->functionOf == 0); const Function *function = findFunctionByName("func", &db->scopeList.front()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == tokenizer.tokens()->next()); ASSERT(function && function->hasBody()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn != scope); ASSERT(function && function->retDef == tokenizer.tokens()->tokAt(5)); } } void hasInlineClassFunction() { GET_SYMBOL_DB("class Fred { void func() { } };\n") // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); if (!scope) return; ASSERT(scope->functionOf && scope->functionOf == db->findScopeByName("Fred")); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && function->isInline()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn == db->findScopeByName("Fred")); ASSERT(function && function->retDef == functionToken->previous()); ASSERT(db && db->findScopeByName("Fred") && db->findScopeByName("Fred")->definedType->getFunction("func") == function); } } void hasInlineClassFunction_trailingReturnType() { GET_SYMBOL_DB("class Fred { auto func() -> int { } };") // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); if (!scope) return; ASSERT(scope->functionOf && scope->functionOf == db->findScopeByName("Fred")); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && function->isInline()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn == db->findScopeByName("Fred")); ASSERT(function && function->retDef == functionToken->tokAt(4)); ASSERT(db && db->findScopeByName("Fred") && db->findScopeByName("Fred")->definedType->getFunction("func") == function); } } void hasMissingInlineClassFunction() { GET_SYMBOL_DB("class Fred { void func(); };\n") // 2 scopes: Global and Class (no Function scope because there is no function implementation) ASSERT(db && db->scopeList.size() == 2); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope == nullptr); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && !function->hasBody()); } } void hasInlineClassOperatorTemplate() { GET_SYMBOL_DB("struct Fred { template Foo & operator=(const Foo &) { return *this; } };"); // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "operator="); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "operator="); if (!scope) return; ASSERT(scope->functionOf && scope->functionOf == db->findScopeByName("Fred")); const Function *function = findFunctionByName("operator=", &db->scopeList.back()); ASSERT(function && function->token->str() == "operator="); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && function->isInline()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn == db->findScopeByName("Fred")); ASSERT(function && function->retDef == functionToken->tokAt(-2)); ASSERT(db && db->findScopeByName("Fred") && db->findScopeByName("Fred")->definedType->getFunction("operator=") == function); } } void hasClassFunction() { GET_SYMBOL_DB("class Fred { void func(); }; void Fred::func() { }\n") // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens()->linkAt(2), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); if (!scope) return; ASSERT(scope->functionOf && scope->functionOf == db->findScopeByName("Fred")); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && !function->isInline()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn == db->findScopeByName("Fred")); } } void hasClassFunction_trailingReturnType() { GET_SYMBOL_DB("class Fred { auto func() -> int; }; auto Fred::func() -> int { }\n"); // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens()->linkAt(2), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); if (!scope) return; ASSERT(scope->functionOf && scope->functionOf == db->findScopeByName("Fred")); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && !function->isInline()); ASSERT(function && function->functionScope == scope && scope->function == function && function->nestedIn == db->findScopeByName("Fred")); } } void hasRegularFunctionReturningFunctionPointer() { GET_SYMBOL_DB("void (*func(int f))(char) { }\n") // 2 scopes: Global and Function ASSERT(db && db->scopeList.size() == 2); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); const Function *function = findFunctionByName("func", &db->scopeList.front()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody()); } } void hasInlineClassFunctionReturningFunctionPointer() { GET_SYMBOL_DB("class Fred { void (*func(int f))(char) { } };\n") // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && function->isInline()); } } void hasMissingInlineClassFunctionReturningFunctionPointer() { GET_SYMBOL_DB("class Fred { void (*func(int f))(char); };\n") // 2 scopes: Global and Class (no Function scope because there is no function implementation) ASSERT(db && db->scopeList.size() == 2); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope == nullptr); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && !function->hasBody()); } } void hasClassFunctionReturningFunctionPointer() { GET_SYMBOL_DB("class Fred { void (*func(int f))(char); }; void (*Fred::func(int f))(char) { }\n") // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens()->linkAt(2), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && !function->isInline()); } } void methodWithRedundantScope() { GET_SYMBOL_DB("class Fred { void Fred::func() {} };\n") // 3 scopes: Global, Class, and Function ASSERT(db && db->scopeList.size() == 3); if (db) { const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); const Scope *scope = findFunctionScopeByToken(db, functionToken); ASSERT(scope && scope->className == "func"); const Function *function = findFunctionByName("func", &db->scopeList.back()); ASSERT(function && function->token->str() == "func"); ASSERT(function && function->token == functionToken); ASSERT(function && function->hasBody() && function->isInline()); } } void complexFunctionArrayPtr() { GET_SYMBOL_DB("int(*p1)[10]; \n" // pointer to array 10 of int "void(*p2)(char); \n" // pointer to function (char) returning void "int(*(*p3)(char))[10];\n" // pointer to function (char) returning pointer to array 10 of int "float(*(*p4)(char))(long); \n" // pointer to function (char) returning pointer to function (long) returning float "short(*(*(*p5) (char))(long))(double);\n" // pointer to function (char) returning pointer to function (long) returning pointer to function (double) returning short "int(*a1[10])(void); \n" // array 10 of pointer to function (void) returning int "float(*(*a2[10])(char))(long);\n" // array 10 of pointer to func (char) returning pointer to func (long) returning float "short(*(*(*a3[10])(char))(long))(double);\n" // array 10 of pointer to function (char) returning pointer to function (long) returning pointer to function (double) returning short "::boost::rational(&r_)[9];\n" // reference to array of ::boost::rational "::boost::rational(&r_)[9];"); // reference to array of ::boost::rational (template!) ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(10, db->getVariableListSize() - 1); ASSERT_EQUALS(true, db->getVariableFromVarId(1) && db->getVariableFromVarId(1)->dimensions().size() == 1); ASSERT_EQUALS(true, db->getVariableFromVarId(2) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(3) && db->getVariableFromVarId(3)->dimensions().size() == 0); ASSERT_EQUALS(true, db->getVariableFromVarId(4) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(5) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(6) && db->getVariableFromVarId(6)->dimensions().size() == 1); ASSERT_EQUALS(true, db->getVariableFromVarId(7) && db->getVariableFromVarId(7)->dimensions().size() == 1); ASSERT_EQUALS(true, db->getVariableFromVarId(8) && db->getVariableFromVarId(8)->dimensions().size() == 1); ASSERT_EQUALS(true, db->getVariableFromVarId(9) && db->getVariableFromVarId(9)->dimensions().size() == 1); ASSERT_EQUALS(true, db->getVariableFromVarId(10) && db->getVariableFromVarId(10)->dimensions().size() == 1); ASSERT_EQUALS("", errout.str()); } } void pointerToMemberFunction() { GET_SYMBOL_DB("bool (A::*pFun)();"); // Pointer to member function of A, returning bool and taking no parameters ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(1, db->getVariableListSize() - 1); ASSERT_EQUALS(true, db->getVariableFromVarId(1) != nullptr); if (db->getVariableFromVarId(1)) ASSERT_EQUALS("pFun", db->getVariableFromVarId(1)->name()); ASSERT_EQUALS("", errout.str()); } } void hasSubClassConstructor() { GET_SYMBOL_DB("class Foo { class Sub; }; class Foo::Sub { Sub() {} };"); ASSERT(db != nullptr); if (db) { bool seen_something = false; for (std::list::const_iterator scope = db->scopeList.begin(); scope != db->scopeList.end(); ++scope) { for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { ASSERT_EQUALS("Sub", func->token->str()); ASSERT_EQUALS(true, func->hasBody()); ASSERT_EQUALS(Function::eConstructor, func->type); seen_something = true; } } ASSERT_EQUALS(true, seen_something); } } void testConstructors() { { GET_SYMBOL_DB("class Foo { Foo(); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Foo f); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eConstructor && !ctor->isExplicit()); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { explicit Foo(Foo f); };"); const Function* ctor = tokenizer.tokens()->tokAt(4)->function(); ASSERT(db && ctor && ctor->type == Function::eConstructor && ctor->isExplicit()); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Bar& f); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Foo& f); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eCopyConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(const Foo &f); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eCopyConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("template class Foo { Foo(Foo& f); };"); const Function* ctor = tokenizer.tokens()->tokAt(7)->function(); ASSERT(db && ctor && ctor->type == Function::eCopyConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Foo& f, int default = 0); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eCopyConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Foo& f, char noDefault); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Foo&& f); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eMoveConstructor); ASSERT(ctor && ctor->retDef == 0); } { GET_SYMBOL_DB("class Foo { Foo(Foo & & f, int default = 1, bool defaultToo = true); };"); const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); ASSERT(db && ctor && ctor->type == Function::eMoveConstructor); ASSERT(ctor && ctor->retDef == 0); } } void functionDeclarationTemplate() { GET_SYMBOL_DB("std::map foo() {}") // 2 scopes: Global and Function ASSERT(db && db->scopeList.size() == 2 && findFunctionByName("foo", &db->scopeList.back())); if (db) { const Scope *scope = &db->scopeList.front(); ASSERT(scope && scope->functionList.size() == 1); const Function *foo = &scope->functionList.front(); ASSERT(foo && foo->token->str() == "foo"); ASSERT(foo && foo->hasBody()); } } void functionDeclarations() { GET_SYMBOL_DB("void foo();\nvoid foo();\nint foo(int i);\nvoid foo() {}") // 2 scopes: Global and Function ASSERT(db && db->scopeList.size() == 2 && findFunctionByName("foo", &db->scopeList.back())); if (db) { const Scope *scope = &db->scopeList.front(); ASSERT(scope && scope->functionList.size() == 2); const Function *foo = &scope->functionList.front(); const Function *foo_int = &scope->functionList.back(); ASSERT(foo && foo->token->str() == "foo"); ASSERT(foo && foo->hasBody()); ASSERT(foo && foo->token->strAt(2) == ")"); ASSERT(foo_int && !foo_int->token); ASSERT(foo_int && foo_int->tokenDef->str() == "foo"); ASSERT(foo_int && !foo_int->hasBody()); ASSERT(foo_int && foo_int->tokenDef->strAt(2) == "int"); ASSERT(&foo_int->argumentList.front() == db->getVariableFromVarId(1)); } } void constructorInitialization() { GET_SYMBOL_DB("std::string logfile;\n" "std::ofstream log(logfile.c_str(), std::ios::out);"); // 1 scope: Global ASSERT(db && db->scopeList.size() == 1); if (db && !db->scopeList.empty()) { // No functions ASSERT(db->scopeList.front().functionList.empty()); } } void memberFunctionOfUnknownClassMacro1() { GET_SYMBOL_DB("class ScVbaFormatCondition { OUString getServiceImplName() SAL_OVERRIDE; };\n" "void ScVbaValidation::getFormula1() {\n" " sal_uInt16 nFlags = 0;\n" " if (pDocSh && !getCellRangesForAddress(nFlags)) ;\n" "}"); ASSERT(db && errout.str().empty()); if (db) { const Scope *scope = db->findScopeByName("getFormula1"); ASSERT(scope != nullptr); ASSERT(scope && scope->nestedIn == &db->scopeList.front()); } } void memberFunctionOfUnknownClassMacro2() { GET_SYMBOL_DB("class ScVbaFormatCondition { OUString getServiceImplName() SAL_OVERRIDE {} };\n" "void getFormula1() {\n" " sal_uInt16 nFlags = 0;\n" " if (pDocSh && !getCellRangesForAddress(nFlags)) ;\n" "}"); ASSERT(db && errout.str().empty()); if (db) { const Scope *scope = db->findScopeByName("getFormula1"); ASSERT(scope != nullptr); ASSERT(scope && scope->nestedIn == &db->scopeList.front()); scope = db->findScopeByName("getServiceImplName"); ASSERT(scope != nullptr); ASSERT(scope && scope->nestedIn && scope->nestedIn->className == "ScVbaFormatCondition"); } } void memberFunctionOfUnknownClassMacro3() { GET_SYMBOL_DB("class ScVbaFormatCondition { OUString getServiceImplName() THROW(whatever); };\n" "void ScVbaValidation::getFormula1() {\n" " sal_uInt16 nFlags = 0;\n" " if (pDocSh && !getCellRangesForAddress(nFlags)) ;\n" "}"); ASSERT(db && errout.str().empty()); if (db) { const Scope *scope = db->findScopeByName("getFormula1"); ASSERT(scope != nullptr); ASSERT(scope && scope->nestedIn == &db->scopeList.front()); } } void functionLinkage() { GET_SYMBOL_DB("static void f1() { }\n" "void f2();\n" "extern void f3();\n" "void f4();\n" "extern void f5() { };\n" "void f6() { }"); ASSERT(db && errout.str().empty()); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "f1"); ASSERT(f && f->function() && f->function()->isStaticLocal() && f->function()->retDef->str() == "void"); f = Token::findsimplematch(tokenizer.tokens(), "f2"); ASSERT(f && f->function() && !f->function()->isStaticLocal() && f->function()->retDef->str() == "void"); f = Token::findsimplematch(tokenizer.tokens(), "f3"); ASSERT(f && f->function() && f->function()->isExtern() && f->function()->retDef->str() == "void"); f = Token::findsimplematch(tokenizer.tokens(), "f4"); ASSERT(f && f->function() && !f->function()->isExtern() && f->function()->retDef->str() == "void"); f = Token::findsimplematch(tokenizer.tokens(), "f5"); ASSERT(f && f->function() && f->function()->isExtern() && f->function()->retDef->str() == "void"); f = Token::findsimplematch(tokenizer.tokens(), "f6"); ASSERT(f && f->function() && !f->function()->isExtern() && f->function()->retDef->str() == "void"); } } void classWithFriend() { GET_SYMBOL_DB("class Foo {}; class Bar1 { friend class Foo; }; class Bar2 { friend Foo; };") // 3 scopes: Global, 3 classes ASSERT(db && db->scopeList.size() == 4); if (db) { const Scope* foo = db->findScopeByName("Foo"); ASSERT(foo != nullptr); const Scope* bar1 = db->findScopeByName("Bar1"); ASSERT(bar1 != nullptr); const Scope* bar2 = db->findScopeByName("Bar2"); ASSERT(bar2 != nullptr); if (foo && bar1 && bar2) { ASSERT(bar1->definedType->friendList.size() == 1 && bar1->definedType->friendList.front().name == "Foo" && bar1->definedType->friendList.front().type == foo->definedType); ASSERT(bar2->definedType->friendList.size() == 1 && bar2->definedType->friendList.front().name == "Foo" && bar2->definedType->friendList.front().type == foo->definedType); } } } void parseFunctionCorrect() { // ticket 3188 - "if" statement parsed as function GET_SYMBOL_DB("void func(i) int i; { if (i == 1) return; }\n") ASSERT(db != nullptr); // 3 scopes: Global, function, if ASSERT_EQUALS(3, db->scopeList.size()); ASSERT(findFunctionByName("func", &db->scopeList.back()) != nullptr); ASSERT(findFunctionByName("if", &db->scopeList.back()) == nullptr); } void parseFunctionDeclarationCorrect() { GET_SYMBOL_DB("void func();\n" "int bar() {}\n" "void func() {}") ASSERT_EQUALS(3, db->findScopeByName("func")->classStart->linenr()); } void Cpp11InitInInitList() { GET_SYMBOL_DB("class Foo {\n" " std::vector bar;\n" " Foo() : bar({\"a\", \"b\"})\n" " {}\n" "};"); ASSERT_EQUALS(4, db->scopeList.front().nestedList.front()->nestedList.front()->classStart->linenr()); } void hasGlobalVariables1() { GET_SYMBOL_DB("int i;\n") ASSERT(db && db->scopeList.size() == 1); if (db && db->scopeList.size() == 1) { std::list::const_iterator it = db->scopeList.begin(); ASSERT(it->varlist.size() == 1); if (it->varlist.size() == 1) { std::list::const_iterator var = it->varlist.begin(); ASSERT(var->name() == "i"); ASSERT(var->typeStartToken()->str() == "int"); } } } void hasGlobalVariables2() { GET_SYMBOL_DB("int array[2][2];\n") ASSERT(db && db->scopeList.size() == 1); if (db && db->scopeList.size() == 1) { std::list::const_iterator it = db->scopeList.begin(); ASSERT(it->varlist.size() == 1); if (it->varlist.size() == 1) { std::list::const_iterator var = it->varlist.begin(); ASSERT(var->name() == "array"); ASSERT(var->typeStartToken()->str() == "int"); } } } void hasGlobalVariables3() { GET_SYMBOL_DB("int array[2][2] = { { 0, 0 }, { 0, 0 } };\n") ASSERT(db && db->scopeList.size() == 1); if (db && db->scopeList.size() == 1) { std::list::const_iterator it = db->scopeList.begin(); ASSERT(it->varlist.size() == 1); if (it->varlist.size() == 1) { std::list::const_iterator var = it->varlist.begin(); ASSERT(var->name() == "array"); ASSERT(var->typeStartToken()->str() == "int"); } } } void checkTypeStartEndToken1() { GET_SYMBOL_DB("static std::string i;\n" "static const std::string j;\n" "const std::string* k;\n" "const char m[];\n" "void f(const char* const l;) {}"); ASSERT(db && db->getVariableListSize() == 6 && db->getVariableFromVarId(1) && db->getVariableFromVarId(2) && db->getVariableFromVarId(3) && db->getVariableFromVarId(4) && db->getVariableFromVarId(5)); if (db && db->getVariableFromVarId(1) && db->getVariableFromVarId(2) && db->getVariableFromVarId(3) && db->getVariableFromVarId(4) && db->getVariableFromVarId(5)) { ASSERT_EQUALS("std", db->getVariableFromVarId(1)->typeStartToken()->str()); ASSERT_EQUALS("std", db->getVariableFromVarId(2)->typeStartToken()->str()); ASSERT_EQUALS("std", db->getVariableFromVarId(3)->typeStartToken()->str()); ASSERT_EQUALS("char", db->getVariableFromVarId(4)->typeStartToken()->str()); ASSERT_EQUALS("char", db->getVariableFromVarId(5)->typeStartToken()->str()); ASSERT_EQUALS("string", db->getVariableFromVarId(1)->typeEndToken()->str()); ASSERT_EQUALS("string", db->getVariableFromVarId(2)->typeEndToken()->str()); ASSERT_EQUALS("*", db->getVariableFromVarId(3)->typeEndToken()->str()); ASSERT_EQUALS("char", db->getVariableFromVarId(4)->typeEndToken()->str()); ASSERT_EQUALS("*", db->getVariableFromVarId(5)->typeEndToken()->str()); } } void checkTypeStartEndToken2() { GET_SYMBOL_DB("class CodeGenerator {\n" " DiagnosticsEngine Diags;\n" "public:\n" " void Initialize() {\n" " Builder.reset(Diags);\n" " }\n" "\n" " void HandleTagDeclRequiredDefinition() LLVM_OVERRIDE {\n" " if (Diags.hasErrorOccurred())\n" " return;\n" " }\n" "};"); ASSERT_EQUALS("DiagnosticsEngine", db->getVariableFromVarId(1)->typeStartToken()->str()); } void checkTypeStartEndToken3() { GET_SYMBOL_DB("void f(const char) {}"); ASSERT(db && db->functionScopes.size()==1U); if (db && db->functionScopes.size()==1U) { const Function * const f = db->functionScopes.front()->function; ASSERT_EQUALS(1U, f->argCount()); const Variable * const arg1 = f->getArgumentVar(0); ASSERT_EQUALS("char", arg1->typeStartToken()->str()); ASSERT_EQUALS("char", arg1->typeEndToken()->str()); } } void check(const char code[], bool debug = true, const char filename[] = "test.cpp") { // Clear the error log errout.str(""); // Check.. settings1.debugwarnings = debug; // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); tokenizer.simplifyTokenList2(); // force symbol database creation tokenizer.createSymbolDatabase(); settings1.debugwarnings = false; } void functionArgs1() { { GET_SYMBOL_DB("void f(std::vector, const std::vector & v) { }"); ASSERT_EQUALS(1+1, db->getVariableListSize()); const Variable* v = db->getVariableFromVarId(1); ASSERT(v && v->isReference() && v->isConst() && v->isArgument()); const Scope* f = db->findScopeByName("f"); ASSERT(f && f->type == Scope::eFunction && f->function); if (f && f->function) ASSERT(f->function->argumentList.size() == 2 && f->function->argumentList.front().index() == 0 && f->function->argumentList.front().name() == "" && f->function->argumentList.back().index() == 1); ASSERT_EQUALS("", errout.str()); } { GET_SYMBOL_DB("void g(std::map > m) { }"); ASSERT_EQUALS(1+1, db->getVariableListSize()); const Variable* m = db->getVariableFromVarId(1); ASSERT(m && !m->isReference() && !m->isConst() && m->isArgument() && m->isClass()); const Scope* g = db->findScopeByName("g"); ASSERT(g && g->type == Scope::eFunction && g->function && g->function->argumentList.size() == 1 && g->function->argumentList.front().index() == 0); ASSERT_EQUALS("", errout.str()); } { GET_SYMBOL_DB("void g(std::map m = std::map()) { }"); const Scope* g = db->findScopeByName("g"); ASSERT(g && g->type == Scope::eFunction && g->function && g->function->argumentList.size() == 1 && g->function->argumentList.front().index() == 0 && g->function->initializedArgCount() == 1); ASSERT_EQUALS("", errout.str()); } { GET_SYMBOL_DB("void g(int = 0) { }"); const Scope* g = db->findScopeByName("g"); ASSERT(g && g->type == Scope::eFunction && g->function && g->function->argumentList.size() == 1 && g->function->argumentList.front().hasDefault()); ASSERT_EQUALS("", errout.str()); } { GET_SYMBOL_DB("void g(int*) { }"); // unnamed pointer argument (#8052) const Scope* g = db->findScopeByName("g"); ASSERT(g && g->type == Scope::eFunction && g->function && g->function->argumentList.size() == 1 && g->function->argumentList.front().nameToken() == nullptr && g->function->argumentList.front().isPointer()); ASSERT_EQUALS("", errout.str()); } { GET_SYMBOL_DB("void g(int* const) { }"); // 'const' is not the name of the variable - #5882 const Scope* g = db->findScopeByName("g"); ASSERT(g && g->type == Scope::eFunction && g->function && g->function->argumentList.size() == 1 && g->function->argumentList.front().nameToken() == nullptr); ASSERT_EQUALS("", errout.str()); } } void functionArgs2() { GET_SYMBOL_DB("void f(int a[][4]) { }"); const Variable *a = db->getVariableFromVarId(1); ASSERT_EQUALS("a", a->nameToken()->str()); ASSERT_EQUALS(2UL, a->dimensions().size()); ASSERT_EQUALS(0UL, a->dimension(0)); ASSERT_EQUALS(false, a->dimensions()[0].known); ASSERT_EQUALS(4UL, a->dimension(1)); ASSERT_EQUALS(true, a->dimensions()[1].known); } void functionArgs3() { GET_SYMBOL_DB("void f(int i,) { }"); // Don't crash const Variable *a = db->getVariableFromVarId(1); ASSERT_EQUALS("i", a->nameToken()->str()); } void functionArgs4() { GET_SYMBOL_DB("void f1(char [10], struct foo [10]);"); ASSERT_EQUALS(true, db->scopeList.front().functionList.size() == 1UL); const Function *func = &db->scopeList.front().functionList.front(); ASSERT_EQUALS(true, func && func->argumentList.size() == 2UL); if (func && func->argumentList.size() == 2UL) { const Variable *first = &func->argumentList.front(); ASSERT_EQUALS(0UL, first->name().size()); ASSERT_EQUALS(1UL, first->dimensions().size()); ASSERT_EQUALS(10UL, first->dimension(0)); const Variable *second = &func->argumentList.back(); ASSERT_EQUALS(0UL, second->name().size()); ASSERT_EQUALS(1UL, second->dimensions().size()); ASSERT_EQUALS(10UL, second->dimension(0)); } } void functionArgs5() { // #7650 GET_SYMBOL_DB("class ABC {};\n" "class Y {\n" " enum ABC {A,B,C};\n" " void f(enum ABC abc) {}\n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "f ( enum"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isEnumType()); } } } } void functionArgs6() { // #7651 GET_SYMBOL_DB("class ABC {};\n" "class Y {\n" " enum ABC {A,B,C};\n" " void f(ABC abc) {}\n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "f ( ABC"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isEnumType()); } } } } void functionArgs7() { // #7652 { GET_SYMBOL_DB("struct AB { int a; int b; };\n" "int foo(struct AB *ab);\n" "void bar() {\n" " struct AB ab;\n" " foo(&ab); \n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->classDef->linenr() == 1); } } } } { GET_SYMBOL_DB("struct AB { int a; int b; };\n" "int foo(AB *ab);\n" "void bar() {\n" " struct AB ab;\n" " foo(&ab); \n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->classDef->linenr() == 1); } } } } { GET_SYMBOL_DB("struct AB { int a; int b; };\n" "int foo(struct AB *ab);\n" "void bar() {\n" " AB ab;\n" " foo(&ab); \n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->classDef->linenr() == 1); } } } } { GET_SYMBOL_DB("struct AB { int a; int b; };\n" "int foo(AB *ab);\n" "void bar() {\n" " AB ab;\n" " foo(&ab); \n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->classDef->linenr() == 1); } } } } } void functionArgs8() { // #7653 GET_SYMBOL_DB("struct A { int i; };\n" "struct B { double d; };\n" "int foo(struct A a);\n" "double foo(struct B b);\n" "void bar() {\n" " struct B b;\n" " foo(b);\n" "}"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( b"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->tokenDef->linenr() == 4 && func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isStructType()); } } } } void functionArgs9() { // #7657 GET_SYMBOL_DB("struct A {\n" " struct B {\n" " enum C { };\n" " };\n" "};\n" "void foo(A::B::C c) { }"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ("); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isEnumType()); } } } } void functionArgs10() { GET_SYMBOL_DB("class Fred {\n" "public:\n" " Fred(Whitespace = PRESERVE_WHITESPACE);\n" "};\n" "Fred::Fred(Whitespace whitespace) { }"); ASSERT_EQUALS(true, db != nullptr); if (db) { ASSERT_EQUALS(3, db->scopeList.size()); if (db->scopeList.size() == 3) { std::list::const_iterator scope = db->scopeList.begin(); ++scope; ASSERT_EQUALS((unsigned int)Scope::eClass, (unsigned int)scope->type); ASSERT_EQUALS(1, scope->functionList.size()); ASSERT(scope->functionList.begin()->functionScope != nullptr); if (scope->functionList.begin()->functionScope) { const Scope * functionScope = scope->functionList.begin()->functionScope; ++scope; ASSERT(functionScope == &*scope); } } } } void functionArgs11() { GET_SYMBOL_DB("class Fred {\n" "public:\n" " void foo(char a[16]);\n" "};\n" "void Fred::foo(char b[16]) { }"); ASSERT_EQUALS(true, db != nullptr); if (db) { ASSERT_EQUALS(3, db->scopeList.size()); if (db->scopeList.size() == 3) { std::list::const_iterator scope = db->scopeList.begin(); ++scope; ASSERT_EQUALS((unsigned int)Scope::eClass, (unsigned int)scope->type); ASSERT_EQUALS(1, scope->functionList.size()); ASSERT(scope->functionList.begin()->functionScope != nullptr); if (scope->functionList.begin()->functionScope) { const Scope * functionScope = scope->functionList.begin()->functionScope; ++scope; ASSERT(functionScope == &*scope); } } } } void functionArgs12() { // #7661 GET_SYMBOL_DB("struct A {\n" " enum E { };\n" " int a[10];\n" "};\n" "struct B : public A {\n" " void foo(B::E e) { }\n" "};"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ("); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isEnumType()); } } } } void functionArgs13() { // #7697 GET_SYMBOL_DB("struct A {\n" " enum E { };\n" " struct S { };\n" "};\n" "struct B : public A {\n" " B(E e);\n" " B(S s);\n" "};\n" "B::B(A::E e) { }\n" "B::B(A::S s) { }"); ASSERT_EQUALS(true, db != nullptr); if (db) { const Token *f = Token::findsimplematch(tokenizer.tokens(), "B ( A :: E"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isEnumType() && type->name() == "E"); } } f = Token::findsimplematch(tokenizer.tokens(), "B ( A :: S"); ASSERT_EQUALS(true, f && f->function()); if (f && f->function()) { const Function *func = f->function(); ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); if (func->argumentList.size() == 1 && func->argumentList.front().type()) { const Type * type = func->argumentList.front().type(); ASSERT_EQUALS(true, type->isStructType() && type->name() == "S"); } } } } void namespaces1() { GET_SYMBOL_DB("namespace fred {\n" " namespace barney {\n" " class X { X(int); };\n" " }\n" "}\n" "namespace barney { X::X(int) { } }"); // Locate the scope for the class.. const Scope *scope = nullptr; for (std::list::const_iterator it = db->scopeList.begin(); it != db->scopeList.end(); ++it) { if (it->isClassOrStruct()) { scope = &(*it); break; } } ASSERT(scope != nullptr); if (!scope) return; ASSERT_EQUALS("X", scope->className); // The class has a constructor but the implementation _is not_ seen ASSERT_EQUALS(1U, scope->functionList.size()); const Function *function = &(scope->functionList.front()); ASSERT_EQUALS(false, function->hasBody()); } // based on namespaces1 but here the namespaces match void namespaces2() { GET_SYMBOL_DB("namespace fred {\n" " namespace barney {\n" " class X { X(int); };\n" " }\n" "}\n" "namespace fred {\n" " namespace barney {\n" " X::X(int) { }\n" " }\n" "}"); // Locate the scope for the class.. const Scope *scope = nullptr; for (std::list::const_iterator it = db->scopeList.begin(); it != db->scopeList.end(); ++it) { if (it->isClassOrStruct()) { scope = &(*it); break; } } ASSERT(scope != nullptr); if (!scope) return; ASSERT_EQUALS("X", scope->className); // The class has a constructor and the implementation _is_ seen ASSERT_EQUALS(1U, scope->functionList.size()); const Function *function = &(scope->functionList.front()); ASSERT_EQUALS("X", function->tokenDef->str()); ASSERT_EQUALS(true, function->hasBody()); } void namespaces3() { // #3854 - namespace with unknown macro GET_SYMBOL_DB("namespace fred UNKNOWN_MACRO(default) {\n" "}"); ASSERT_EQUALS(2U, db->scopeList.size()); ASSERT_EQUALS(Scope::eGlobal, db->scopeList.front().type); ASSERT_EQUALS(Scope::eNamespace, db->scopeList.back().type); } void namespaces4() { // #4698 - type lookup GET_SYMBOL_DB("struct A { int a; };\n" "namespace fred { struct A {}; }\n" "fred::A fredA;"); const Variable *fredA = db->getVariableFromVarId(2U); ASSERT_EQUALS("fredA", fredA->name()); const Type *fredAType = fredA->type(); ASSERT_EQUALS(2U, fredAType->classDef->linenr()); } void tryCatch1() { const char str[] = "void foo() {\n" " try { }\n" " catch (const Error1 & x) { }\n" " catch (const X::Error2 & x) { }\n" " catch (Error3 x) { }\n" " catch (X::Error4 x) { }\n" "}"; GET_SYMBOL_DB(str); ASSERT_EQUALS("", errout.str()); ASSERT(db && db->getVariableListSize() == 5); // index 0 + 4 variables ASSERT(db && db->scopeList.size() == 7); // global + function + try + 4 catch } void symboldatabase1() { check("namespace foo {\n" " class bar;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class foo : public bar < int, int> {\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase2() { check("class foo {\n" "public slots :\n" "foo() { }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class foo {\n" "class bar;\n" "foo() { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase3() { check("typedef void (func_type)();\n" "struct A {\n" " friend func_type f : 2;\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase4() { check("static void function_declaration_before(void) __attribute__((__used__));\n" "static void function_declaration_before(void) {}\n" "static void function_declaration_after(void) {}\n" "static void function_declaration_after(void) __attribute__((__used__));\n"); ASSERT_EQUALS("", errout.str()); check("main(int argc, char *argv[]) { }", true, "test.c"); ASSERT_EQUALS("[test.c:1]: (debug) SymbolDatabase::isFunction found C function 'main' without a return type.\n", errout.str()); check("namespace boost {\n" " std::locale generate_locale()\n" " {\n" " return std::locale();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace X {\n" " static void function_declaration_before(void) __attribute__((__used__));\n" " static void function_declaration_before(void) {}\n" " static void function_declaration_after(void) {}\n" " static void function_declaration_after(void) __attribute__((__used__));\n" "}"); ASSERT_EQUALS("", errout.str()); check("testing::testing()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (debug) Executable scope 'testing' with unknown function.\n", errout.str()); } void symboldatabase5() { // ticket #2178 - segmentation fault check("int CL_INLINE_DECL(integer_decode_float) (int x) {\n" " return (sign ? cl_I() : 0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void symboldatabase6() { // ticket #2221 - segmentation fault check("template class X { };\n" "X< 1>2 > x1;\n" "X<(1>2)> x2;\n" "template class Y { };\n" "Y> x3;\n" "Y>1>> x4;\n" "Y>1)>> x5;\n", false); ASSERT_EQUALS("", errout.str()); } void symboldatabase7() { // ticket #2230 - segmentation fault check("template class E,class D> class C : E\n" "{\n" "public:\n" " int f();\n" "};\n" "class E : C\n" "{\n" "public:\n" " int f() { return C< ::D,int>::f(); }\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (debug) simplifyTemplates: bailing out\n", errout.str()); } void symboldatabase8() { // ticket #2252 - segmentation fault check("struct PaletteColorSpaceHolder: public rtl::StaticWithInit,\n" " PaletteColorSpaceHolder>\n" "{\n" " uno::Reference operator()()\n" " {\n" " return vcl::unotools::createStandardColorSpace();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase9() { // ticket #2425 - segmentation fault check("class CHyperlink : public CString\n" "{\n" "public:\n" " const CHyperlink& operator=(LPCTSTR lpsz) {\n" " CString::operator=(lpsz);\n" " return *this;\n" " }\n" "};\n", false); ASSERT_EQUALS("", errout.str()); } void symboldatabase10() { // ticket #2537 - segmentation fault check("class A {\n" "private:\n" " void f();\n" "};\n" "class B {\n" " friend void A::f();\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase11() { // ticket #2539 - segmentation fault check("int g ();\n" "struct S {\n" " int i : (false ? g () : 1);\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase12() { // ticket #2547 - segmentation fault check("class foo {\n" " void bar2 () = __null;\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase13() { // ticket #2577 - segmentation fault check("class foo {\n" " void bar2 () = A::f;\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase14() { // ticket #2589 - segmentation fault ASSERT_THROW(check("struct B : A\n"), InternalError); } void symboldatabase17() { // ticket #2657 - segmentation fault check("return f(){}"); ASSERT_EQUALS("", errout.str()); } void symboldatabase19() { // ticket #2991 - segmentation fault check("::y(){x}"); ASSERT_EQUALS("", errout.str()); } void symboldatabase20() { // ticket #3013 - segmentation fault ASSERT_THROW(check("struct x : virtual y\n"), InternalError); } void symboldatabase21() { check("class Fred {\n" " class Foo { };\n" " void func() const;\n" "};\n" "Fred::func() const {\n" " Foo foo;\n" "}"); ASSERT_EQUALS("", errout.str()); } // ticket 3437 (segmentation fault) void symboldatabase22() { check("template struct A {};\n" "A a;\n"); ASSERT_EQUALS("", errout.str()); } // ticket 3435 (std::vector) void symboldatabase23() { GET_SYMBOL_DB("class A { std::vector ints; };"); ASSERT_EQUALS(2U, db->scopeList.size()); const Scope &scope = db->scopeList.back(); ASSERT_EQUALS(1U, scope.varlist.size()); const Variable &var = scope.varlist.front(); ASSERT_EQUALS(std::string("ints"), var.name()); ASSERT_EQUALS(true, var.isClass()); } // ticket 3508 (constructor, destructor) void symboldatabase24() { GET_SYMBOL_DB("struct Fred {\n" " ~Fred();\n" " Fred();\n" "};\n" "Fred::Fred() { }\n" "Fred::~Fred() { }"); // Global scope, Fred, Fred::Fred, Fred::~Fred ASSERT_EQUALS(4U, db->scopeList.size()); // Find the scope for the Fred struct.. const Scope *fredScope = nullptr; for (std::list::const_iterator scope = db->scopeList.begin(); scope != db->scopeList.end(); ++scope) { if (scope->isClassOrStruct() && scope->className == "Fred") fredScope = &(*scope); } ASSERT(fredScope != nullptr); if (fredScope == nullptr) return; // The struct Fred has two functions, a constructor and a destructor ASSERT_EQUALS(2U, fredScope->functionList.size()); // Get linenumbers where the bodies for the constructor and destructor are.. unsigned int constructor = 0; unsigned int destructor = 0; for (std::list::const_iterator it = fredScope->functionList.begin(); it != fredScope->functionList.end(); ++it) { if (it->type == Function::eConstructor) constructor = it->token->linenr(); // line number for constructor body if (it->type == Function::eDestructor) destructor = it->token->linenr(); // line number for destructor body } // The body for the constructor is located at line 5.. ASSERT_EQUALS(5U, constructor); // The body for the destructor is located at line 6.. ASSERT_EQUALS(6U, destructor); } // ticket #3561 (throw C++) void symboldatabase25() { const char str[] = "int main() {\n" " foo bar;\n" " throw bar;\n" "}"; GET_SYMBOL_DB(str); ASSERT_EQUALS("", errout.str()); ASSERT(db && db->getVariableListSize() == 2); // index 0 + 1 variable } // ticket #3561 (throw C) void symboldatabase26() { const char str[] = "int main() {\n" " throw bar;\n" "}"; GET_SYMBOL_DB_C(str); ASSERT_EQUALS("", errout.str()); ASSERT(db && db->getVariableListSize() == 2); // index 0 + 1 variable } // ticket #3543 (segmentation fault) void symboldatabase27() { check("class C : public B1\n" "{\n" " B1()\n" " {} C(int) : B1() class\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase28() { GET_SYMBOL_DB("struct S {};\n" "void foo(struct S s) {}"); ASSERT(db && db->getVariableFromVarId(1) && db->getVariableFromVarId(1)->typeScope() && db->getVariableFromVarId(1)->typeScope()->className == "S"); } // ticket #4442 (segmentation fault) void symboldatabase29() { check("struct B : A {\n" " B() : A {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase30() { GET_SYMBOL_DB("struct A { void foo(const int a); };\n" "void A::foo(int a) { }"); ASSERT(db && db->functionScopes.size() == 1 && db->functionScopes[0]->functionOf); } void symboldatabase31() { GET_SYMBOL_DB("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"); ASSERT(db && db->typeList.size() == 5); if (!db || db->typeList.size() < 5) return; std::list::const_iterator i = db->typeList.begin(); const Type* Foo = &(*i++); const Type* Bar = &(*i++); const Type* Sub = &(*i++); const Type* Foo_Sub = &(*i++); const Type* Bar_Sub = &(*i); ASSERT(Foo && Foo->classDef && Foo->classScope && Foo->enclosingScope && Foo->name() == "Foo"); ASSERT(Bar && Bar->classDef && Bar->classScope && Bar->enclosingScope && Bar->name() == "Bar"); ASSERT(Sub && Sub->classDef && !Sub->classScope && Sub->enclosingScope && Sub->name() == "Sub"); ASSERT(Foo_Sub && Foo_Sub->classDef && Foo_Sub->classScope && Foo_Sub->enclosingScope == Foo->classScope && Foo_Sub->name() == "Sub"); ASSERT(Bar_Sub && Bar_Sub->classDef && Bar_Sub->classScope && Bar_Sub->enclosingScope == Bar->classScope && Bar_Sub->name() == "Sub"); ASSERT(Foo_Sub && Foo_Sub->classScope && Foo_Sub->classScope->numConstructors == 1 && Foo_Sub->classScope->className == "Sub"); ASSERT(Bar_Sub && Bar_Sub->classScope && Bar_Sub->classScope->numConstructors == 2 && Bar_Sub->classScope->className == "Sub"); } void symboldatabase32() { GET_SYMBOL_DB("struct Base {\n" " void foo() {}\n" "};\n" "class Deri : Base {\n" "};"); ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->definedType->getFunction("foo")); } void symboldatabase33() { // ticket #4682 GET_SYMBOL_DB("static struct A::B s;\n" "static struct A::B t = { 0 };\n" "static struct A::B u(0);\n" "static struct A::B v{0};\n" "static struct A::B w({0});\n" "void foo() { }"); ASSERT(db && db->functionScopes.size() == 1); } void symboldatabase34() { // ticket #4694 check("typedef _Atomic(int(A::*)) atomic_mem_ptr_to_int;\n" "typedef _Atomic(int)&atomic_int_ref;\n" "struct S {\n" " _Atomic union { int n; };\n" "};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase35() { // ticket #4806 and #4841 check("class FragmentQueue : public CL_NS(util)::PriorityQueue >\n" "{};"); ASSERT_EQUALS("", errout.str()); } void symboldatabase36() { // ticket #4892 check("void struct ( ) { if ( 1 ) } int main ( ) { }"); ASSERT_EQUALS("", errout.str()); } void symboldatabase37() { GET_SYMBOL_DB("class Fred {\n" "public:\n" " struct Wilma { };\n" " struct Barney {\n" " bool operator == (const struct Barney & b) const { return true; }\n" " bool operator == (const struct Wilma & w) const { return true; }\n" " };\n" " Fred(const struct Barney & b) { barney = b; }\n" "private:\n" " struct Barney barney;\n" "};\n"); ASSERT(db && db->typeList.size() == 3); if (!db || db->typeList.size() != 3) return; std::list::const_iterator i = db->typeList.begin(); const Type* Fred = &(*i++); const Type* Wilma = &(*i++); const Type* Barney = &(*i++); ASSERT(Fred && Fred->classDef && Fred->classScope && Fred->enclosingScope && Fred->name() == "Fred"); ASSERT(Wilma && Wilma->classDef && Wilma->classScope && Wilma->enclosingScope && Wilma->name() == "Wilma"); ASSERT(Barney && Barney->classDef && Barney->classScope && Barney->enclosingScope && Barney->name() == "Barney"); ASSERT(db->getVariableListSize() == 5); if (db->getVariableListSize() != 5) return; ASSERT(db->getVariableFromVarId(1) && db->getVariableFromVarId(1)->type() && db->getVariableFromVarId(1)->type()->name() == "Barney"); ASSERT(db->getVariableFromVarId(2) && db->getVariableFromVarId(2)->type() && db->getVariableFromVarId(2)->type()->name() == "Wilma"); ASSERT(db->getVariableFromVarId(3) && db->getVariableFromVarId(3)->type() && db->getVariableFromVarId(3)->type()->name() == "Barney"); } void symboldatabase38() { // ticket #5125 check("template struct scoped_service;\n" "struct service {};\n" "template <> struct scoped_service {};\n" "template \n" "struct scoped_service : scoped_service\n" "{\n" " scoped_service( T* ptr ) : scoped_service(ptr), m_ptr(ptr) {}\n" " T* const m_ptr;\n" "};"); } void symboldatabase40() { // ticket #5153 check("void f() {\n" " try { }\n" " catch (std::bad_alloc) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void symboldatabase41() { // ticket #5197 (unknown macro) GET_SYMBOL_DB("struct X1 { MACRO1 f(int spd) MACRO2; };\n"); ASSERT(db && db->findScopeByName("X1") && db->findScopeByName("X1")->functionList.size() == 1 && !db->findScopeByName("X1")->functionList.front().hasBody()); } void symboldatabase42() { // only put variables in variable list GET_SYMBOL_DB("void f() { extern int x(); }\n"); ASSERT(db != nullptr); const Scope * const fscope = db ? db->findScopeByName("f") : nullptr; ASSERT(fscope != nullptr); ASSERT_EQUALS(0U, fscope ? fscope->varlist.size() : ~0U); // "x" is not a variable } void symboldatabase43() { // ticket #4738 check("void f() {\n" " new int;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void symboldatabase44() { GET_SYMBOL_DB("int i { 1 };\n" "int j ( i );\n" "void foo() {\n" " int k { 1 };\n" " int l ( 1 );\n" "}"); ASSERT(db != nullptr); ASSERT_EQUALS(4U, db->getVariableListSize() - 1); ASSERT_EQUALS(2U, db->scopeList.size()); for (std::size_t i = 1U; i < db->getVariableListSize(); i++) ASSERT(db->getVariableFromVarId(i) != nullptr); } void symboldatabase45() { GET_SYMBOL_DB("typedef struct {\n" " unsigned long bits;\n" "} S;\n" "struct T {\n" " S span;\n" " int flags;\n" "};\n" "struct T f(int x) {\n" " return (struct T) {\n" " .span = (S) { 0UL },\n" " .flags = (x ? 256 : 0),\n" " };\n" "}"); ASSERT(db != nullptr); ASSERT_EQUALS(4U, db->getVariableListSize() - 1); for (std::size_t i = 1U; i < db->getVariableListSize(); i++) ASSERT(db->getVariableFromVarId(i) != nullptr); ASSERT_EQUALS(4U, db->scopeList.size()); std::list::const_iterator scope = db->scopeList.begin(); ASSERT_EQUALS(Scope::eGlobal, scope->type); ++scope; ASSERT_EQUALS(Scope::eStruct, scope->type); ++scope; ASSERT_EQUALS(Scope::eStruct, scope->type); ++scope; ASSERT_EQUALS(Scope::eFunction, scope->type); } void symboldatabase46() { // #6171 (anonymous namespace) GET_SYMBOL_DB("struct S { };\n" "namespace {\n" " struct S { };\n" "}"); ASSERT(db != nullptr); ASSERT_EQUALS(4U, db->scopeList.size()); std::list::const_iterator scope = db->scopeList.begin(); ASSERT_EQUALS(Scope::eGlobal, scope->type); ++scope; ASSERT_EQUALS(Scope::eStruct, scope->type); ASSERT_EQUALS(scope->className, "S"); ++scope; ASSERT_EQUALS(Scope::eNamespace, scope->type); ASSERT_EQUALS(scope->className, ""); ++scope; ASSERT_EQUALS(Scope::eStruct, scope->type); ASSERT_EQUALS(scope->className, "S"); } void symboldatabase47() { // #6308 - associate Function and Scope for destructors GET_SYMBOL_DB("namespace NS {\n" " class MyClass {\n" " ~MyClass();\n" " };\n" "}\n" "using namespace NS;\n" "MyClass::~MyClass() {\n" " delete Example;\n" "}"); ASSERT(db && !db->functionScopes.empty() && db->functionScopes.front()->function && db->functionScopes.front()->function->functionScope == db->functionScopes.front()); } void symboldatabase48() { // #6417 GET_SYMBOL_DB("namespace NS {\n" " class MyClass {\n" " MyClass();\n" " ~MyClass();\n" " };\n" "}\n" "using namespace NS;\n" "MyClass::~MyClass() { }\n" "MyClass::MyClass() { }\n"); ASSERT(db && !db->functionScopes.empty() && db->functionScopes.front()->function && db->functionScopes.front()->function->functionScope == db->functionScopes.front()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "MyClass ( ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3 && f->function()->token->linenr() == 9); f = Token::findsimplematch(tokenizer.tokens(), "~ MyClass ( ) ;"); f = f->next(); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4 && f->function()->token->linenr() == 8); } void symboldatabase49() { // #6424 GET_SYMBOL_DB("namespace Ns { class C; }\n" "void f1() { char *p; *p = 0; }\n" "class Ns::C* p;\n" "void f2() { char *p; *p = 0; }\n"); ASSERT(db != nullptr); const Token *f = Token::findsimplematch(tokenizer.tokens(), "p ; void f2"); ASSERT_EQUALS(true, db && f && f->variable()); f = Token::findsimplematch(tokenizer.tokens(), "f2"); ASSERT_EQUALS(true, db && f && f->function()); } void symboldatabase50() { // #6432 GET_SYMBOL_DB("template \n" "class _ConstTessMemberResultCallback_0_0\n" " {\n" " public:\n" " typedef void (T::*MemberSignature)() const;\n" "\n" " private:\n" " const T* object_;\n" " MemberSignature member_;\n" "\n" " public:\n" " inline _ConstTessMemberResultCallback_0_0(\n" " const T* object, MemberSignature member)\n" " : object_(object),\n" " member_(member) {\n" " }\n" "};"); ASSERT(db != nullptr); const Token *f = Token::findsimplematch(tokenizer.tokens(), "_ConstTessMemberResultCallback_0_0 ("); ASSERT_EQUALS(true, db && f && f->function() && f->function()->isConstructor()); } void symboldatabase51() { // #6538 GET_SYMBOL_DB("static const float f1 = 2 * foo1(a, b);\n" "static const float f2 = 2 * ::foo2(a, b);\n" "static const float f3 = 2 * std::foo3(a, b);\n" "static const float f4 = c * foo4(a, b);\n" "static const int i1 = 2 & foo5(a, b);\n" "static const bool b1 = 2 > foo6(a, b);\n"); ASSERT(db != nullptr); if (db) { ASSERT(findFunctionByName("foo1", &db->scopeList.front()) == nullptr); ASSERT(findFunctionByName("foo2", &db->scopeList.front()) == nullptr); ASSERT(findFunctionByName("foo3", &db->scopeList.front()) == nullptr); ASSERT(findFunctionByName("foo4", &db->scopeList.front()) == nullptr); ASSERT(findFunctionByName("foo5", &db->scopeList.front()) == nullptr); ASSERT(findFunctionByName("foo6", &db->scopeList.front()) == nullptr); } } void symboldatabase52() { // #6581 GET_SYMBOL_DB("void foo() {\n" " int i = 0;\n" " S s{ { i }, 0 };\n" "}"); ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(2, db->scopeList.size()); ASSERT_EQUALS(2, db->getVariableListSize()-1); ASSERT(db->getVariableFromVarId(1) != nullptr); ASSERT(db->getVariableFromVarId(2) != nullptr); } } void symboldatabase53() { // #7124 GET_SYMBOL_DB("int32_t x;" "std::int32_t y;"); ASSERT(db != nullptr); if (db) { ASSERT(db->getVariableFromVarId(1) != nullptr); ASSERT(db->getVariableFromVarId(2) != nullptr); ASSERT_EQUALS(false, db->getVariableFromVarId(1)->isClass()); ASSERT_EQUALS(false, db->getVariableFromVarId(2)->isClass()); } } void symboldatabase54() { // #7343 GET_SYMBOL_DB("class A {\n" " void getReg() const override {\n" " assert(Kind == k_ShiftExtend);\n" " }\n" "};"); ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(1U, db->functionScopes.size()); ASSERT_EQUALS("getReg", db->functionScopes.front()->className); } } void symboldatabase55() { // #7767 GET_SYMBOL_DB("PRIVATE S32 testfunc(void) {\n" " return 0;\n" "}"); ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(1U, db->functionScopes.size()); ASSERT_EQUALS("testfunc", db->functionScopes.front()->className); } } void symboldatabase56() { // #7909 { GET_SYMBOL_DB("class Class {\n" " class NestedClass {\n" " public:\n" " virtual void f();\n" " };\n" " friend void NestedClass::f();\n" "}"); ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(0U, db->functionScopes.size()); ASSERT(db->scopeList.back().type == Scope::eClass && db->scopeList.back().className == "NestedClass"); ASSERT(db->scopeList.back().functionList.size() == 1U && !db->scopeList.back().functionList.front().hasBody()); } } { GET_SYMBOL_DB("class Class {\n" " friend void f1();\n" " friend void f2() { }\n" "}"); ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(1U, db->functionScopes.size()); ASSERT(db->scopeList.back().type == Scope::eFunction && db->scopeList.back().className == "f2"); ASSERT(db->scopeList.back().function && db->scopeList.back().function->hasBody()); } } { GET_SYMBOL_DB_C("friend f1();\n" "friend f2() { }\n"); ASSERT(db != nullptr); if (db) { ASSERT_EQUALS(2U, db->scopeList.size()); ASSERT_EQUALS(2U, db->scopeList.begin()->functionList.size()); } } } void symboldatabase57() { GET_SYMBOL_DB("int bar(bool b)\n" "{\n" " if(b)\n" " return 1;\n" " else\n" " return 1;\n" "}"); ASSERT(db != nullptr); if (db) { ASSERT(db->scopeList.size() == 4U); if (db->scopeList.size() == 4U) { std::list::const_iterator it = db->scopeList.begin(); ASSERT(it->type == Scope::eGlobal); ASSERT((++it)->type == Scope::eFunction); ASSERT((++it)->type == Scope::eIf); ASSERT((++it)->type == Scope::eElse); } } } void symboldatabase58() { // #6985 (using namespace type lookup) GET_SYMBOL_DB("namespace N2\n" "{\n" "class B { };\n" "}\n" "using namespace N2;\n" "class C {\n" " class A : public B\n" " {\n" " };\n" "};"); ASSERT(db != nullptr); if (db) { ASSERT(db->typeList.size() == 3U); if (db->typeList.size() == 3U) { std::list::const_iterator it = db->typeList.begin(); const Type * classB = &(*it); const Type * classC = &(*(++it)); const Type * classA = &(*(++it)); ASSERT(classA->name() == "A" && classB->name() == "B" && classC->name() == "C"); if (classA->name() == "A" && classB->name() == "B" && classC->name() == "C") { ASSERT(classA->derivedFrom.size() == 1U); if (classA->derivedFrom.size() == 1) { ASSERT(classA->derivedFrom[0].type != nullptr); if (classA->derivedFrom[0].type != nullptr) { ASSERT(classA->derivedFrom[0].type == classB); } } } } } } void enum1() { GET_SYMBOL_DB("enum BOOL { FALSE, TRUE }; enum BOOL b;"); /* there is a enum scope with the name BOOL */ ASSERT(db && db->scopeList.back().type == Scope::eEnum && db->scopeList.back().className == "BOOL"); /* b is a enum variable, type is BOOL */ ASSERT(db && db->getVariableFromVarId(1)->isEnumType()); } void enum2() { GET_SYMBOL_DB("enum BOOL { FALSE, TRUE } b;"); /* there is a enum scope with the name BOOL */ ASSERT(db && db->scopeList.back().type == Scope::eEnum && db->scopeList.back().className == "BOOL"); /* b is a enum variable, type is BOOL */ ASSERT(db && db->getVariableFromVarId(1)->isEnumType()); } void enum3() { GET_SYMBOL_DB("enum ABC { A=11,B,C=A+B };"); ASSERT(db && db->scopeList.back().type == Scope::eEnum); if (db) { /* There is an enum A with value 11 */ const Enumerator *A = db->scopeList.back().findEnumerator("A"); ASSERT(A && A->value==11 && A->value_known); /* There is an enum B with value 12 */ const Enumerator *B = db->scopeList.back().findEnumerator("B"); ASSERT(B && B->value==12 && B->value_known); /* There is an enum C with value 23 */ const Enumerator *C = db->scopeList.back().findEnumerator("C"); ASSERT(C && C->value==23 && C->value_known); } } void enum4() { // #7493 GET_SYMBOL_DB("enum Offsets { O1, O2, O3=5, O4 };\n" "enum MyEnums { E1=O1+1, E2, E3=O3+1 };"); ASSERT(db != nullptr); if (!db) return; ASSERT_EQUALS(3U, db->scopeList.size()); // Assert that all enum values are known std::list::const_iterator scope = db->scopeList.begin(); // Offsets ++scope; ASSERT_EQUALS((unsigned int)Scope::eEnum, (unsigned int)scope->type); ASSERT_EQUALS(4U, scope->enumeratorList.size()); ASSERT(scope->enumeratorList[0].name->enumerator() == &scope->enumeratorList[0]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[0].name->tokType()); ASSERT(scope->enumeratorList[0].scope == &*scope); ASSERT_EQUALS("O1", scope->enumeratorList[0].name->str()); ASSERT(scope->enumeratorList[0].start == nullptr); ASSERT(scope->enumeratorList[0].end == nullptr); ASSERT_EQUALS(true, scope->enumeratorList[0].value_known); ASSERT_EQUALS(0, scope->enumeratorList[0].value); ASSERT(scope->enumeratorList[1].name->enumerator() == &scope->enumeratorList[1]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[1].name->tokType()); ASSERT(scope->enumeratorList[1].scope == &*scope); ASSERT_EQUALS("O2", scope->enumeratorList[1].name->str()); ASSERT(scope->enumeratorList[1].start == nullptr); ASSERT(scope->enumeratorList[1].end == nullptr); ASSERT_EQUALS(true, scope->enumeratorList[1].value_known); ASSERT_EQUALS(1, scope->enumeratorList[1].value); ASSERT(scope->enumeratorList[2].name->enumerator() == &scope->enumeratorList[2]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[2].name->tokType()); ASSERT(scope->enumeratorList[2].scope == &*scope); ASSERT_EQUALS("O3", scope->enumeratorList[2].name->str()); ASSERT(scope->enumeratorList[2].start != nullptr); ASSERT(scope->enumeratorList[2].end != nullptr); ASSERT_EQUALS(true, scope->enumeratorList[2].value_known); ASSERT_EQUALS(5, scope->enumeratorList[2].value); ASSERT(scope->enumeratorList[3].name->enumerator() == &scope->enumeratorList[3]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[3].name->tokType()); ASSERT(scope->enumeratorList[3].scope == &*scope); ASSERT_EQUALS("O4", scope->enumeratorList[3].name->str()); ASSERT(scope->enumeratorList[3].start == nullptr); ASSERT(scope->enumeratorList[3].end == nullptr); ASSERT_EQUALS(true, scope->enumeratorList[3].value_known); ASSERT_EQUALS(6, scope->enumeratorList[3].value); // MyEnums ++scope; ASSERT_EQUALS((unsigned int)Scope::eEnum, (unsigned int)scope->type); ASSERT_EQUALS(3U, scope->enumeratorList.size()); ASSERT(scope->enumeratorList[0].name->enumerator() == &scope->enumeratorList[0]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[0].name->tokType()); ASSERT(scope->enumeratorList[0].scope == &*scope); ASSERT_EQUALS("E1", scope->enumeratorList[0].name->str()); ASSERT(scope->enumeratorList[0].start != nullptr); ASSERT(scope->enumeratorList[0].end != nullptr); ASSERT_EQUALS(true, scope->enumeratorList[0].value_known); ASSERT_EQUALS(1, scope->enumeratorList[0].value); ASSERT(scope->enumeratorList[1].name->enumerator() == &scope->enumeratorList[1]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[1].name->tokType()); ASSERT(scope->enumeratorList[1].scope == &*scope); ASSERT_EQUALS("E2", scope->enumeratorList[1].name->str()); ASSERT(scope->enumeratorList[1].start == nullptr); ASSERT(scope->enumeratorList[1].end == nullptr); ASSERT_EQUALS(true, scope->enumeratorList[1].value_known); ASSERT_EQUALS(2, scope->enumeratorList[1].value); ASSERT(scope->enumeratorList[2].name->enumerator() == &scope->enumeratorList[2]); ASSERT_EQUALS((unsigned int)Token::eEnumerator, (unsigned int)scope->enumeratorList[2].name->tokType()); ASSERT(scope->enumeratorList[2].scope == &*scope); ASSERT_EQUALS("E3", scope->enumeratorList[2].name->str()); ASSERT(scope->enumeratorList[2].start != nullptr); ASSERT(scope->enumeratorList[2].end != nullptr); ASSERT_EQUALS(true, scope->enumeratorList[2].value_known); ASSERT_EQUALS(6, scope->enumeratorList[2].value); } void enum5() { GET_SYMBOL_DB("enum { A = 10, B = 2 };\n" "int a[10 + 2];\n" "int b[A];\n" "int c[A + 2];\n" "int d[10 + B];\n" "int e[A + B];\n"); ASSERT(db != nullptr); if (!db) return; ASSERT_EQUALS(2U, db->scopeList.size()); // Assert that all enum values are known std::list::const_iterator scope = db->scopeList.begin(); ++scope; ASSERT_EQUALS((unsigned int)Scope::eEnum, (unsigned int)scope->type); ASSERT_EQUALS(2U, scope->enumeratorList.size()); ASSERT_EQUALS(true, scope->enumeratorList[0].value_known); ASSERT_EQUALS(10, scope->enumeratorList[0].value); ASSERT_EQUALS(true, scope->enumeratorList[1].value_known); ASSERT_EQUALS(2, scope->enumeratorList[1].value); ASSERT(db->getVariableListSize() == 6); // the first one is not used const Variable * v = db->getVariableFromVarId(1); ASSERT(v != nullptr); if (!v) return; ASSERT(v->isArray()); ASSERT_EQUALS(1U, v->dimensions().size()); ASSERT_EQUALS(12U, v->dimension(0)); v = db->getVariableFromVarId(2); ASSERT(v != nullptr); if (!v) return; ASSERT(v->isArray()); ASSERT_EQUALS(1U, v->dimensions().size()); ASSERT_EQUALS(10U, v->dimension(0)); v = db->getVariableFromVarId(3); ASSERT(v != nullptr); if (!v) return; ASSERT(v->isArray()); ASSERT_EQUALS(1U, v->dimensions().size()); ASSERT_EQUALS(12U, v->dimension(0)); v = db->getVariableFromVarId(4); ASSERT(v != nullptr); if (!v) return; ASSERT(v->isArray()); ASSERT_EQUALS(1U, v->dimensions().size()); ASSERT_EQUALS(12U, v->dimension(0)); v = db->getVariableFromVarId(5); ASSERT(v != nullptr); if (!v) return; ASSERT(v->isArray()); ASSERT_EQUALS(1U, v->dimensions().size()); ASSERT_EQUALS(12U, v->dimension(0)); } void enum6() { GET_SYMBOL_DB("struct Fred {\n" " enum Enum { E0, E1 };\n" "};\n" "struct Barney : public Fred {\n" " Enum func(Enum e) { return e; }\n" "};"); ASSERT(db != nullptr); if (!db) return; const Token * const functionToken = Token::findsimplematch(tokenizer.tokens(), "func"); ASSERT(functionToken != nullptr); if (!functionToken) return; const Function *function = functionToken->function(); ASSERT(function != nullptr); if (!function) return; ASSERT(function->token->str() == "func"); ASSERT(function->retDef && function->retDef->str() == "Enum"); ASSERT(function->retType && function->retType->name() == "Enum"); } #define TEST(S) \ v = db->getVariableFromVarId(id++); \ ASSERT(v != nullptr); \ if (!v) \ return; \ ASSERT(v->isArray()); \ ASSERT_EQUALS(1U, v->dimensions().size()); \ ASSERT_EQUALS(S, v->dimension(0)) void enum7() { GET_SYMBOL_DB("enum E { X };\n" "enum EC : char { C };\n" "enum ES : short { S };\n" "enum EI : int { I };\n" "enum EL : long { L };\n" "enum ELL : long long { LL };\n" "char array1[sizeof(E)];\n" "char array2[sizeof(X)];\n" "char array3[sizeof(EC)];\n" "char array4[sizeof(C)];\n" "char array5[sizeof(ES)];\n" "char array6[sizeof(S)];\n" "char array7[sizeof(EI)];\n" "char array8[sizeof(I)];\n" "char array9[sizeof(EL)];\n" "char array10[sizeof(L)];\n" "char array11[sizeof(ELL)];\n" "char array12[sizeof(LL)];\n"); ASSERT(db != nullptr); if (!db) return; ASSERT(db->getVariableListSize() == 13); // the first one is not used const Variable * v; unsigned int id = 1; TEST(settings1.sizeof_int); TEST(settings1.sizeof_int); TEST(1); TEST(1); TEST(settings1.sizeof_short); TEST(settings1.sizeof_short); TEST(settings1.sizeof_int); TEST(settings1.sizeof_int); TEST(settings1.sizeof_long); TEST(settings1.sizeof_long); TEST(settings1.sizeof_long_long); TEST(settings1.sizeof_long_long); } void sizeOfType() { // #7615 - crash in Symboldatabase::sizeOfType() GET_SYMBOL_DB("enum e;\n" "void foo() {\n" " e abc[] = {A,B,C};\n" " int i = abc[ARRAY_SIZE(cats)];\n" "}"); const Token *e = Token::findsimplematch(tokenizer.tokens(), "e abc"); db->sizeOfType(e); // <- don't crash } void isImplicitlyVirtual() { { GET_SYMBOL_DB("class Base {\n" " virtual void foo() {}\n" "};\n" "class Deri : Base {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual()); } { GET_SYMBOL_DB("class Base {\n" " virtual void foo() {}\n" "};\n" "class Deri1 : Base {\n" " void foo() {}\n" "};\n" "class Deri2 : Deri1 {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri2") && db->findScopeByName("Deri2")->functionList.front().isImplicitlyVirtual()); } { GET_SYMBOL_DB("class Base {\n" " void foo() {}\n" "};\n" "class Deri : Base {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri") && !db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(true)); } { GET_SYMBOL_DB("class Base {\n" " virtual void foo() {}\n" "};\n" "class Deri : Base {\n" " void foo(std::string& s) {}\n" "};"); ASSERT(db && db->findScopeByName("Deri") && !db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(true)); } { GET_SYMBOL_DB("class Base {\n" " virtual void foo() {}\n" "};\n" "class Deri1 : Base {\n" " void foo(int i) {}\n" "};\n" "class Deri2 : Deri1 {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri2") && db->findScopeByName("Deri2")->functionList.front().isImplicitlyVirtual()); } { GET_SYMBOL_DB("class Base : Base2 {\n" // We don't know Base2 " void foo() {}\n" "};\n" "class Deri : Base {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(true)); // Default true -> true } { GET_SYMBOL_DB("class Base : Base2 {\n" // We don't know Base2 " void foo() {}\n" "};\n" "class Deri : Base {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri") && !db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(false)); // Default false -> false } { GET_SYMBOL_DB("class Base : Base2 {\n" // We don't know Base2 " virtual void foo() {}\n" "};\n" "class Deri : Base {\n" " void foo() {}\n" "};"); ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(false)); // Default false, but we saw "virtual" -> true } // #5289 { GET_SYMBOL_DB("template<>\n" "class Bar {\n" "};\n" "template\n" "class Bar : private Bar {\n" " void foo() {\n" " }\n" "};"); ASSERT(db && db->findScopeByName("Bar") && !db->findScopeByName("Bar")->functionList.front().isImplicitlyVirtual(false)); if (db) ASSERT_EQUALS(1, db->findScopeByName("Bar")->functionList.size()); } // #5590 { GET_SYMBOL_DB("class InfiniteB : InfiniteA {\n" " class D {\n" " };\n" "};\n" "namespace N {\n" " class InfiniteA : InfiniteB {\n" " };\n" "}\n" "class InfiniteA : InfiniteB {\n" " void foo();\n" "};\n" "void InfiniteA::foo() {\n" " C a;\n" "}"); //ASSERT(db && db->findScopeByName("InfiniteA") && !db->findScopeByName("InfiniteA")->functionList.front().isImplicitlyVirtual()); TODO_ASSERT_EQUALS(1, 0, db->findScopeByName("InfiniteA")->functionList.size()); } } void isPure() { GET_SYMBOL_DB("class C {\n" " void f() = 0;\n" " C(B b) = 0;\n" " C(C& c) = default;" " void g();\n" "};"); ASSERT(db && db->scopeList.back().functionList.size() == 4); if (db && db->scopeList.back().functionList.size() == 4) { std::list::const_iterator it = db->scopeList.back().functionList.begin(); ASSERT((it++)->isPure()); ASSERT((it++)->isPure()); ASSERT(!(it++)->isPure()); ASSERT(!(it++)->isPure()); } } void isFunction() { // #5602 - UNKNOWN_MACRO(a,b) { .. } GET_SYMBOL_DB("TEST(a,b) {\n" " std::vector messages;\n" " foo(messages[2].size());\n" "}"); const Variable * const var = db ? db->getVariableFromVarId(1U) : nullptr; ASSERT(db && db->findScopeByName("TEST") && var && var->typeStartToken() && var->typeStartToken()->str() == "std"); } void findFunction1() { GET_SYMBOL_DB("int foo(int x);\n" /* 1 */ "void foo();\n" /* 2 */ "void bar() {\n" /* 3 */ " foo();\n" /* 4 */ " foo(1);\n" /* 5 */ "}"); /* 6 */ ASSERT_EQUALS("", errout.str()); if (db) { const Scope * bar = db->findScopeByName("bar"); ASSERT(bar != nullptr); if (bar) { unsigned int linenrs[] = { 2, 1 }; unsigned int index = 0; for (const Token * tok = bar->classStart->next(); tok != bar->classEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (") && !tok->varId() && Token::simpleMatch(tok->linkAt(1), ") ;")) { const Function * function = db->findFunction(tok); ASSERT(function != nullptr); if (function) { std::stringstream expected; expected << "Function call on line " << tok->linenr() << " calls function on line " << linenrs[index] << std::endl; std::stringstream actual; actual << "Function call on line " << tok->linenr() << " calls function on line " << function->tokenDef->linenr() << std::endl; ASSERT_EQUALS(expected.str(), actual.str()); } index++; } } } } } void findFunction2() { // The function does not match the function call. GET_SYMBOL_DB("void func(const int x, const Fred &fred);\n" "void otherfunc() {\n" " float t;\n" " func(x, &t);\n" "}"); const Token *callfunc = Token::findsimplematch(tokenizer.tokens(), "func ( x , & t ) ;"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null ASSERT_EQUALS(true, callfunc != nullptr); // not null ASSERT_EQUALS(false, (callfunc && callfunc->function())); // callfunc->function() should be null } void findFunction3() { GET_SYMBOL_DB("struct base { void foo() { } };\n" "struct derived : public base { void foo() { } };\n" "void foo() {\n" " derived d;\n" " d.foo();\n" "}"); const Token *callfunc = Token::findsimplematch(tokenizer.tokens(), "d . foo ( ) ;"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null ASSERT_EQUALS(true, callfunc != nullptr); // not null ASSERT_EQUALS(true, callfunc && callfunc->tokAt(2)->function() && callfunc->tokAt(2)->function()->tokenDef->linenr() == 2); // should find function on line 2 } void findFunction4() { GET_SYMBOL_DB("void foo(UNKNOWN) { }\n" "void foo(int a) { }\n" "void foo(unsigned int a) { }\n" "void foo(unsigned long a) { }\n" "void foo(unsigned long long a) { }\n" "void foo(float a) { }\n" "void foo(double a) { }\n" "void foo(long double a) { }\n" "int i;\n" "unsigned int ui;\n" "unsigned long ul;\n" "unsigned long long ull;\n" "float f;\n" "double d;\n" "long double ld;\n" "int & ri = i;\n" "unsigned int & rui = ui;\n" "unsigned long & rul = ul;\n" "unsigned long long & rull = ull;\n" "float & rf = f;\n" "double & rd = d;\n" "long double & rld = ld;\n" "const int & cri = i;\n" "const unsigned int & crui = ui;\n" "const unsigned long & crul = ul;\n" "const unsigned long long & crull = ull;\n" "const float & crf = f;\n" "const double & crd = d;\n" "const long double & crld = ld;\n" "void foo() {\n" " foo(1);\n" " foo(1U);\n" " foo(1UL);\n" " foo(1ULL);\n" " foo(1.0F);\n" " foo(1.0);\n" " foo(1.0L);\n" " foo(i);\n" " foo(ui);\n" " foo(ul);\n" " foo(ull);\n" " foo(f);\n" " foo(d);\n" " foo(ld);\n" " foo(ri);\n" " foo(rui);\n" " foo(rul);\n" " foo(rull);\n" " foo(rf);\n" " foo(rd);\n" " foo(rld);\n" " foo(cri);\n" " foo(crui);\n" " foo(crul);\n" " foo(crull);\n" " foo(crf);\n" " foo(crd);\n" " foo(crld);\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1 ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1U ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1UL ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1ULL ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1.0F ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1.0 ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1.0L ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); f = Token::findsimplematch(tokenizer.tokens(), "foo ( i ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ui ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ul ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ull ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( f ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( d ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ld ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ri ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rui ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rul ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rull ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rf ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rd ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rld ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cri ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crui ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crul ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crull ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crf ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crd ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crld ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); } void findFunction5() { GET_SYMBOL_DB("struct Fred {\n" " void Sync(dsmp_t& type, int& len, int limit = 123);\n" " void Sync(int& syncpos, dsmp_t& type, int& len, int limit = 123);\n" " void FindSyncPoint();\n" "};\n" "void Fred::FindSyncPoint() {\n" " dsmp_t type;\n" " int syncpos, len;\n" " Sync(syncpos, type, len);\n" " Sync(type, len);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "Sync ( syncpos"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "Sync ( type"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); } void findFunction6() { // avoid null pointer access GET_SYMBOL_DB("void addtoken(Token** rettail, const Token *tok);\n" "void CheckMemoryLeakInFunction::getcode(const Token *tok ) {\n" " addtoken(&rettail, tok);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "void addtoken ( Token * *"); ASSERT_EQUALS(true, db && f && !f->function()); // regression value only } void findFunction7() { GET_SYMBOL_DB("class ResultEnsemble {\n" "public:\n" " std::vector &nodeResults() const;\n" " std::vector &nodeResults();\n" "};\n" "class Simulator {\n" " int generatePinchResultEnsemble(const ResultEnsemble &power, const ResultEnsemble &ground) {\n" " power.nodeResults().size();\n" " assert(power.nodeResults().size()==ground.nodeResults().size());\n" " }\n" "};") const Token *callfunc = Token::findsimplematch(tokenizer.tokens(), "power . nodeResults ( ) . size ( ) ;"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null ASSERT_EQUALS(true, callfunc != nullptr); // not null ASSERT_EQUALS(true, callfunc && callfunc->tokAt(2)->function() && callfunc->tokAt(2)->function()->tokenDef->linenr() == 3); } void findFunction8() { GET_SYMBOL_DB("struct S {\n" " void f() { }\n" " void f() & { }\n" " void f() &&{ }\n" " void f() const { }\n" " void f() const & { }\n" " void f() const &&{ }\n" " void g() ;\n" " void g() & ;\n" " void g() &&;\n" " void g() const ;\n" " void g() const & ;\n" " void g() const &&;\n" "};\n" "void S::g() { }\n" "void S::g() & { }\n" "void S::g() &&{ }\n" "void S::g() const { }\n" "void S::g() const & { }\n" "void S::g() const &&{ }\n"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "f ( ) {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "f ( ) & {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "f ( ) && {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "f ( ) const {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "f ( ) const & {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "f ( ) const && {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "g ( ) {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8 && f->function()->token->linenr() == 15); f = Token::findsimplematch(tokenizer.tokens(), "g ( ) & {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 9 && f->function()->token->linenr() == 16); f = Token::findsimplematch(tokenizer.tokens(), "g ( ) && {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 10 && f->function()->token->linenr() == 17); f = Token::findsimplematch(tokenizer.tokens(), "g ( ) const {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 11 && f->function()->token->linenr() == 18); f = Token::findsimplematch(tokenizer.tokens(), "g ( ) const & {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 12 && f->function()->token->linenr() == 19); f = Token::findsimplematch(tokenizer.tokens(), "g ( ) const && {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 13 && f->function()->token->linenr() == 20); f = Token::findsimplematch(tokenizer.tokens(), "S :: g ( ) {"); ASSERT_EQUALS(true, db && f && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 8 && f->tokAt(2)->function()->token->linenr() == 15); f = Token::findsimplematch(tokenizer.tokens(), "S :: g ( ) & {"); ASSERT_EQUALS(true, db && f && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 9 && f->tokAt(2)->function()->token->linenr() == 16); f = Token::findsimplematch(tokenizer.tokens(), "S :: g ( ) && {"); ASSERT_EQUALS(true, db && f && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 10 && f->tokAt(2)->function()->token->linenr() == 17); f = Token::findsimplematch(tokenizer.tokens(), "S :: g ( ) const {"); ASSERT_EQUALS(true, db && f && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 11 && f->tokAt(2)->function()->token->linenr() == 18); f = Token::findsimplematch(tokenizer.tokens(), "S :: g ( ) const & {"); ASSERT_EQUALS(true, db && f && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 12 && f->tokAt(2)->function()->token->linenr() == 19); f = Token::findsimplematch(tokenizer.tokens(), "S :: g ( ) const && {"); ASSERT_EQUALS(true, db && f && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 13 && f->tokAt(2)->function()->token->linenr() == 20); } void findFunction9() { GET_SYMBOL_DB("struct Fred {\n" " void foo(const int * p);\n" "};\n" "void Fred::foo(const int * const p) { }"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( const int * const p ) {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); } void findFunction10() { // #7673 GET_SYMBOL_DB("struct Fred {\n" " void foo(const int * p);\n" "};\n" "void Fred::foo(const int p []) { }"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( const int p [ ] ) {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); } void findFunction11() { GET_SYMBOL_DB("class Fred : public QObject {\n" " Q_OBJECT\n" "private slots:\n" " void foo();\n" "};\n" "void Fred::foo() { }"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( ) {"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); } void findFunction12() { GET_SYMBOL_DB("void foo(std::string a) { }\n" "void foo(long long a) { }\n" "void func(char* cp) {\n" " foo(0);\n" " foo(0L);\n" " foo(0.f);\n" " foo(bar());\n" " foo(cp);\n" " foo(\"\");\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( 0 ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 0L ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 0.f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( bar ( ) ) ;"); ASSERT_EQUALS(true, f && f->function() == nullptr); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cp ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 1); f = Token::findsimplematch(tokenizer.tokens(), "foo ( \"\" ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 1); } void findFunction13() { GET_SYMBOL_DB("void foo(std::string a) { }\n" "void foo(double a) { }\n" "void foo(long long a) { }\n" "void foo(int* a) { }\n" "void foo(void* a) { }\n" "void func(int i, const float f, int* ip, float* fp, char* cp) {\n" " foo(0);\n" " foo(0L);\n" " foo(0.f);\n" " foo(false);\n" " foo(bar());\n" " foo(i);\n" " foo(f);\n" " foo(&i);\n" " foo(&f);\n" " foo(ip);\n" " foo(fp);\n" " foo(cp);\n" " foo(\"\");\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( 0 ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 0L ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 0.f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( false ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( bar ( ) ) ;"); ASSERT_EQUALS(true, f && f->function() == nullptr); f = Token::findsimplematch(tokenizer.tokens(), "foo ( i ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( & i ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( & f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ip ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( fp ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cp ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( \"\" ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 5); } void findFunction14() { GET_SYMBOL_DB("void foo(int* a) { }\n" "void foo(const int* a) { }\n" "void foo(void* a) { }\n" "void foo(const float a) { }\n" "void foo(bool a) { }\n" "void foo2(Foo* a) { }\n" "void foo2(Foo a) { }\n" "void func(int* ip, const int* cip, const char* ccp, char* cp, float f, bool b) {\n" " foo(ip);\n" " foo(cip);\n" " foo(cp);\n" " foo(ccp);\n" " foo(f);\n" " foo(b);\n" " foo2(0);\n" " foo2(nullptr);\n" " foo2(NULL);\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( ip ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cip ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cp ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ccp ) ;"); ASSERT_EQUALS(true, f && f->function() == nullptr); f = Token::findsimplematch(tokenizer.tokens(), "foo ( f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( b ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( 0 ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( nullptr ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( NULL ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 6); } void findFunction15() { GET_SYMBOL_DB("void foo1(int, char* a) { }\n" "void foo1(int, char a) { }\n" "void foo1(int, wchar_t a) { }\n" "void foo2(int, float a) { }\n" "void foo2(int, wchar_t a) { }\n" "void foo3(int, float a) { }\n" "void foo3(int, char a) { }\n" "void func() {\n" " foo1(1, 'c');\n" " foo1(2, L'c');\n" " foo2(3, 'c');\n" " foo2(4, L'c');\n" " foo3(5, 'c');\n" " foo3(6, L'c');\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo1 ( 1"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo1 ( 2"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( 3"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( 4"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo3 ( 5"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo3 ( 6"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); } void findFunction16() { GET_SYMBOL_DB("struct C { int i; static int si; float f; int* ip; float* fp};\n" "void foo(float a) { }\n" "void foo(int a) { }\n" "void foo(int* a) { }\n" "void func(C c, C* cp) {\n" " foo(c.i);\n" " foo(cp->i);\n" " foo(c.f);\n" " foo(c.si);\n" " foo(C::si);\n" " foo(c.ip);\n" " foo(c.fp);\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( c . i ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cp . i ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( c . f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( c . si ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( C :: si ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( c . ip ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( c . fp ) ;"); ASSERT_EQUALS(true, f && f->function() == nullptr); } void findFunction17() { GET_SYMBOL_DB("void foo(int a) { }\n" "void foo(float a) { }\n" "void foo(void* a) { }\n" "void foo(bool a) { }\n" "void func(int i, float f, bool b) {\n" " foo(i + i);\n" " foo(f + f);\n" " foo(!b);\n" " foo(i > 0);\n" " foo(f + i);\n" "}"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( i + i ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); f = Token::findsimplematch(tokenizer.tokens(), "foo ( f + f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ! b ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( i > 0 ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( f + i ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 2); } void findFunction18() { GET_SYMBOL_DB("class Fred {\n" " void f(int i) { }\n" " void f(float f) const { }\n" " void a() { f(1); }\n" " void b() { f(1.f); }\n" "};"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "f ( 1 ) ;"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "f ( 1.f ) ;"); ASSERT_EQUALS(true, f && f->function() && f->function()->tokenDef->linenr() == 3); } #define FUNC(x) const Function *x = findFunctionByName(#x, &db->scopeList.front()); \ ASSERT_EQUALS(true, x != nullptr); \ if (x) ASSERT_EQUALS(true, x->isNoExcept()); void noexceptFunction1() { GET_SYMBOL_DB("void func1() noexcept;\n" "void func2() noexcept { }\n" "void func3() noexcept(true);\n" "void func4() noexcept(true) { }\n"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { FUNC(func1); FUNC(func2); FUNC(func3); FUNC(func4); } } void noexceptFunction2() { GET_SYMBOL_DB("template void self_assign(T& t) noexcept(noexcept(t = t)) {t = t; }\n"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { FUNC(self_assign); } } #define CLASS_FUNC(x, y, z) const Function *x = findFunctionByName(#x, y); \ ASSERT_EQUALS(true, x != nullptr); \ if (x) ASSERT_EQUALS(z, x->isNoExcept()); void noexceptFunction3() { GET_SYMBOL_DB("struct Fred {\n" " void func1() noexcept;\n" " void func2() noexcept { }\n" " void func3() noexcept(true);\n" " void func4() noexcept(true) { }\n" " void func5() const noexcept;\n" " void func6() const noexcept { }\n" " void func7() const noexcept(true);\n" " void func8() const noexcept(true) { }\n" " void func9() noexcept = 0;\n" " void func10() noexcept = 0;\n" " void func11() const noexcept(true) = 0;\n" " void func12() const noexcept(true) = 0;\n" " void func13() const noexcept(false) = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { const Scope *fred = db->findScopeByName("Fred"); ASSERT_EQUALS(true, fred != nullptr); if (fred) { CLASS_FUNC(func1, fred, true); CLASS_FUNC(func2, fred, true); CLASS_FUNC(func3, fred, true); CLASS_FUNC(func4, fred, true); CLASS_FUNC(func5, fred, true); CLASS_FUNC(func6, fred, true); CLASS_FUNC(func7, fred, true); CLASS_FUNC(func8, fred, true); CLASS_FUNC(func9, fred, true); CLASS_FUNC(func10, fred, true); CLASS_FUNC(func11, fred, true); CLASS_FUNC(func12, fred, true); CLASS_FUNC(func13, fred, false); } } } void noexceptFunction4() { GET_SYMBOL_DB("class A {\n" "public:\n" " A(A&& a) {\n" " throw std::runtime_error(\"err\");\n" " }\n" "};\n" "class B {\n" " A a;\n" " B(B&& b) noexcept\n" " :a(std::move(b.a)) { }\n" "};\n"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { const Scope *b = db->findScopeByName("B"); ASSERT_EQUALS(true, b != nullptr); if (b) { CLASS_FUNC(B, b, true); } } } #define FUNC_THROW(x) const Function *x = findFunctionByName(#x, &db->scopeList.front()); \ ASSERT_EQUALS(true, x != nullptr); \ if (x) ASSERT_EQUALS(true, x->isThrow()); void throwFunction1() { GET_SYMBOL_DB("void func1() throw();\n" "void func2() throw() { }\n" "void func3() throw(int);\n" "void func4() throw(int) { }\n"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { FUNC_THROW(func1); FUNC_THROW(func2); FUNC_THROW(func3); FUNC_THROW(func4); } } #define CLASS_FUNC_THROW(x, y) const Function *x = findFunctionByName(#x, y); \ ASSERT_EQUALS(true, x != nullptr); \ if (x) ASSERT_EQUALS(true, x->isThrow()); void throwFunction2() { GET_SYMBOL_DB("struct Fred {\n" " void func1() throw();\n" " void func2() throw() { }\n" " void func3() throw(int);\n" " void func4() throw(int) { }\n" " void func5() const throw();\n" " void func6() const throw() { }\n" " void func7() const throw(int);\n" " void func8() const throw(int) { }\n" " void func9() throw() = 0;\n" " void func10() throw(int) = 0;\n" " void func11() const throw() = 0;\n" " void func12() const throw(int) = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { const Scope *fred = db->findScopeByName("Fred"); ASSERT_EQUALS(true, fred != nullptr); if (fred) { CLASS_FUNC_THROW(func1, fred); CLASS_FUNC_THROW(func2, fred); CLASS_FUNC_THROW(func3, fred); CLASS_FUNC_THROW(func4, fred); CLASS_FUNC_THROW(func5, fred); CLASS_FUNC_THROW(func6, fred); CLASS_FUNC_THROW(func7, fred); CLASS_FUNC_THROW(func8, fred); CLASS_FUNC_THROW(func9, fred); CLASS_FUNC_THROW(func10, fred); CLASS_FUNC_THROW(func11, fred); CLASS_FUNC_THROW(func12, fred); } } } void nothrowAttributeFunction() { GET_SYMBOL_DB("void func() __attribute__((nothrow));\n" "void func() { }\n"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { const Function *func = findFunctionByName("func", &db->scopeList.front()); ASSERT_EQUALS(true, func != nullptr); if (func) ASSERT_EQUALS(true, func->isAttributeNothrow()); } } void nothrowDeclspecFunction() { GET_SYMBOL_DB("void __declspec(nothrow) func() { }\n"); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(true, db != nullptr); // not null if (db) { const Function *func = findFunctionByName("func", &db->scopeList.front()); ASSERT_EQUALS(true, func != nullptr); if (func) ASSERT_EQUALS(true, func->isAttributeNothrow()); } } void varTypesIntegral() { GET_SYMBOL_DB("void f() { bool b; char c; unsigned char uc; short s; unsigned short us; int i; unsigned u; unsigned int ui; long l; unsigned long ul; long long ll; }"); const Variable *b = db->getVariableFromVarId(1); ASSERT(b != nullptr); if (b) { ASSERT_EQUALS("b", b->nameToken()->str()); ASSERT_EQUALS(false, b->isFloatingType()); } const Variable *c = db->getVariableFromVarId(2); ASSERT(c != nullptr); if (c) { ASSERT_EQUALS("c", c->nameToken()->str()); ASSERT_EQUALS(false, c->isFloatingType()); } const Variable *uc = db->getVariableFromVarId(3); ASSERT(uc != nullptr); if (uc) { ASSERT_EQUALS("uc", uc->nameToken()->str()); ASSERT_EQUALS(false, uc->isFloatingType()); } const Variable *s = db->getVariableFromVarId(4); ASSERT(s != nullptr); if (s) { ASSERT_EQUALS("s", s->nameToken()->str()); ASSERT_EQUALS(false, s->isFloatingType()); } const Variable *us = db->getVariableFromVarId(5); ASSERT(us != nullptr); if (us) { ASSERT_EQUALS("us", us->nameToken()->str()); ASSERT_EQUALS(false, us->isFloatingType()); } const Variable *i = db->getVariableFromVarId(6); ASSERT(i != nullptr); if (i) { ASSERT_EQUALS("i", i->nameToken()->str()); ASSERT_EQUALS(false, i->isFloatingType()); } const Variable *u = db->getVariableFromVarId(7); ASSERT(u != nullptr); if (u) { ASSERT_EQUALS("u", u->nameToken()->str()); ASSERT_EQUALS(false, u->isFloatingType()); } const Variable *ui = db->getVariableFromVarId(8); ASSERT(ui != nullptr); if (ui) { ASSERT_EQUALS("ui", ui->nameToken()->str()); ASSERT_EQUALS(false, ui->isFloatingType()); } const Variable *l = db->getVariableFromVarId(9); ASSERT(l != nullptr); if (l) { ASSERT_EQUALS("l", l->nameToken()->str()); ASSERT_EQUALS(false, l->isFloatingType()); } const Variable *ul = db->getVariableFromVarId(10); ASSERT(ul != nullptr); if (ul) { ASSERT_EQUALS("ul", ul->nameToken()->str()); ASSERT_EQUALS(false, ul->isFloatingType()); } const Variable *ll = db->getVariableFromVarId(11); ASSERT(ll != nullptr); if (ll) { ASSERT_EQUALS("ll", ll->nameToken()->str()); ASSERT_EQUALS(false, ll->isFloatingType()); } } void varTypesFloating() { { GET_SYMBOL_DB("void f() { float f; double d; long double ld; }"); const Variable *f = db->getVariableFromVarId(1); ASSERT(f != nullptr); if (f) { ASSERT_EQUALS("f", f->nameToken()->str()); ASSERT_EQUALS(true, f->isFloatingType()); } const Variable *d = db->getVariableFromVarId(2); ASSERT(d != nullptr); if (d) { ASSERT_EQUALS("d", d->nameToken()->str()); ASSERT_EQUALS(true, d->isFloatingType()); } const Variable *ld = db->getVariableFromVarId(3); ASSERT(ld != nullptr); if (ld) { ASSERT_EQUALS("ld", ld->nameToken()->str()); ASSERT_EQUALS(true, ld->isFloatingType()); } } { GET_SYMBOL_DB("void f() { float * f; static const float * scf; }"); const Variable *f = db->getVariableFromVarId(1); ASSERT(f != nullptr); if (f) { ASSERT_EQUALS("f", f->nameToken()->str()); ASSERT_EQUALS(true, f->isFloatingType()); ASSERT_EQUALS(true, f->isArrayOrPointer()); } const Variable *scf = db->getVariableFromVarId(2); ASSERT(scf != nullptr); if (scf) { ASSERT_EQUALS("scf", scf->nameToken()->str()); ASSERT_EQUALS(true, scf->isFloatingType()); ASSERT_EQUALS(true, scf->isArrayOrPointer()); } } { GET_SYMBOL_DB("void f() { float fa[42]; }"); const Variable *fa = db->getVariableFromVarId(1); ASSERT(fa != nullptr); if (fa) { ASSERT_EQUALS("fa", fa->nameToken()->str()); ASSERT_EQUALS(true, fa->isFloatingType()); ASSERT_EQUALS(true, fa->isArrayOrPointer()); } } } void varTypesOther() { GET_SYMBOL_DB("void f() { class A {} a; void *b; }"); const Variable *a = db->getVariableFromVarId(1); ASSERT(a != nullptr); if (a) { ASSERT_EQUALS("a", a->nameToken()->str()); ASSERT_EQUALS(false, a->isFloatingType()); } const Variable *b = db->getVariableFromVarId(2); ASSERT(b != nullptr); if (b) { ASSERT_EQUALS("b", b->nameToken()->str()); ASSERT_EQUALS(false, b->isFloatingType()); } } void functionPrototype() { check("int foo(int x) {\n" " extern int func1();\n" " extern int func2(int);\n" " int func3();\n" " int func4(int);\n" " return func4(x);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void lambda() { GET_SYMBOL_DB("void func() {\n" " float y = 0.0f;\n" " auto lambda = [&]()\n" " {\n" " float x = 1.0f;\n" " y += 10.0f - x;\n" " }\n" " lambda();\n" "}"); ASSERT(db && db->scopeList.size() == 3); if (db && db->scopeList.size() == 3) { std::list::const_iterator scope = db->scopeList.begin(); ASSERT_EQUALS(Scope::eGlobal, scope->type); ++scope; ASSERT_EQUALS(Scope::eFunction, scope->type); ++scope; ASSERT_EQUALS(Scope::eLambda, scope->type); } } void lambda2() { GET_SYMBOL_DB("void func() {\n" " float y = 0.0f;\n" " auto lambda = [&]() -> bool\n" " {\n" " float x = 1.0f;\n" " }\n" " lambda();\n" "}"); ASSERT(db && db->scopeList.size() == 3); if (db && db->scopeList.size() == 3) { std::list::const_iterator scope = db->scopeList.begin(); ASSERT_EQUALS(Scope::eGlobal, scope->type); ++scope; ASSERT_EQUALS(Scope::eFunction, scope->type); ++scope; ASSERT_EQUALS(Scope::eLambda, scope->type); } } // #6298 "stack overflow in Scope::findFunctionInBase (endless recursion)" void circularDependencies() { check("template class E,class D> class C : E {\n" " public:\n" " int f();\n" "};\n" "class E : C {\n" " public:\n" " int f() { return C< ::D,int>::f(); }\n" "};\n" "int main() {\n" " E c;\n" " c.f();\n" "}"); } void executableScopeWithUnknownFunction() { GET_SYMBOL_DB("class Fred {\n" " void foo(const std::string & a = "");\n" "};\n" "Fred::foo(const std::string & b) { }\n"); ASSERT(db && db->scopeList.size() == 3); if (db && db->scopeList.size() == 3) { std::list::const_iterator scope = db->scopeList.begin(); ASSERT_EQUALS(Scope::eGlobal, scope->type); ++scope; ASSERT_EQUALS(Scope::eClass, scope->type); const Scope * class_scope = &*scope; ++scope; ASSERT(class_scope->functionList.size() == 1); if (class_scope->functionList.size() == 1) { ASSERT(class_scope->functionList.begin()->hasBody()); ASSERT(class_scope->functionList.begin()->functionScope == &*scope); } } } std::string typeOf(const char code[], const char pattern[], const char filename[] = "test.cpp", const Settings *settings = nullptr) { Tokenizer tokenizer(settings ? settings : &settings2, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); const Token* tok; for (tok = tokenizer.list.back(); tok; tok = tok->previous()) if (Token::simpleMatch(tok, pattern)) break; return tok->valueType() ? tok->valueType()->str() : std::string(); } void valuetype() { // stringification ASSERT_EQUALS("", ValueType().str()); Settings s; s.int_bit = 16; s.long_bit = 32; s.long_long_bit = 64; // numbers ASSERT_EQUALS("signed int", typeOf("1;", "1", "test.c", &s)); ASSERT_EQUALS("signed int", typeOf("32767;", "32767", "test.c", &s)); ASSERT_EQUALS("signed long", typeOf("32768;", "32768", "test.c", &s)); ASSERT_EQUALS("unsigned int", typeOf("32768U;", "32768U", "test.c", &s)); ASSERT_EQUALS("signed long long", typeOf("2147483648;", "2147483648", "test.c", &s)); ASSERT_EQUALS("unsigned int", typeOf("1U;", "1U")); ASSERT_EQUALS("signed long", typeOf("1L;", "1L")); ASSERT_EQUALS("unsigned long", typeOf("1UL;", "1UL")); ASSERT_EQUALS("signed long long", typeOf("1LL;", "1LL")); ASSERT_EQUALS("unsigned long long", typeOf("1ULL;", "1ULL")); ASSERT_EQUALS("unsigned long long", typeOf("1LLU;", "1LLU")); ASSERT_EQUALS("signed long long", typeOf("1i64;", "1i64")); ASSERT_EQUALS("unsigned long long", typeOf("1ui64;", "1ui64")); ASSERT_EQUALS("unsigned int", typeOf("1u;", "1u")); ASSERT_EQUALS("signed long", typeOf("1l;", "1l")); ASSERT_EQUALS("unsigned long", typeOf("1ul;", "1ul")); ASSERT_EQUALS("signed long long", typeOf("1ll;", "1ll")); ASSERT_EQUALS("unsigned long long", typeOf("1ull;", "1ull")); ASSERT_EQUALS("unsigned long long", typeOf("1llu;", "1llu")); ASSERT_EQUALS("float", typeOf("1.0F;", "1.0F")); ASSERT_EQUALS("float", typeOf("1.0f;", "1.0f")); ASSERT_EQUALS("double", typeOf("1.0;", "1.0")); ASSERT_EQUALS("double", typeOf("1E3;", "1E3")); ASSERT_EQUALS("long double", typeOf("1.23L;", "1.23L")); // Constant calculations ASSERT_EQUALS("signed int", typeOf("1 + 2;", "+")); ASSERT_EQUALS("signed long", typeOf("1L + 2;", "+")); ASSERT_EQUALS("signed long long", typeOf("1LL + 2;", "+")); ASSERT_EQUALS("float", typeOf("1.2f + 3;", "+")); ASSERT_EQUALS("float", typeOf("1 + 2.3f;", "+")); // promotions ASSERT_EQUALS("signed int", typeOf("(char)1 + (char)2;", "+")); ASSERT_EQUALS("signed int", typeOf("(short)1 + (short)2;", "+")); ASSERT_EQUALS("signed int", typeOf("(signed int)1 + (signed char)2;", "+")); ASSERT_EQUALS("signed int", typeOf("(signed int)1 + (unsigned char)2;", "+")); ASSERT_EQUALS("unsigned int", typeOf("(unsigned int)1 + (signed char)2;", "+")); ASSERT_EQUALS("unsigned int", typeOf("(unsigned int)1 + (unsigned char)2;", "+")); ASSERT_EQUALS("unsigned int", typeOf("(unsigned int)1 + (signed int)2;", "+")); ASSERT_EQUALS("unsigned int", typeOf("(unsigned int)1 + (unsigned int)2;", "+")); ASSERT_EQUALS("signed long", typeOf("(signed long)1 + (unsigned int)2;", "+")); ASSERT_EQUALS("unsigned long", typeOf("(unsigned long)1 + (signed int)2;", "+")); // char * ASSERT_EQUALS("const char *", typeOf("\"hello\" + 1;", "+")); ASSERT_EQUALS("const char", typeOf("\"hello\"[1];", "[")); ASSERT_EQUALS("const char", typeOf(";*\"hello\";", "*")); ASSERT_EQUALS("const short *", typeOf("L\"hello\" + 1;", "+")); // Variable calculations ASSERT_EQUALS("void *", typeOf("void *p; a = p + 1;", "+")); ASSERT_EQUALS("signed int", typeOf("int x; a = x + 1;", "+")); ASSERT_EQUALS("signed int", typeOf("int x; a = x | 1;", "|")); ASSERT_EQUALS("float", typeOf("float x; a = x + 1;", "+")); ASSERT_EQUALS("signed int", typeOf("signed x; a = x + 1;", "x +")); ASSERT_EQUALS("unsigned int", typeOf("unsigned x; a = x + 1;", "x +")); ASSERT_EQUALS("unsigned int", typeOf("unsigned int u1, u2; a = u1 + 1;", "u1 +")); ASSERT_EQUALS("unsigned int", typeOf("unsigned int u1, u2; a = u1 + 1U;", "u1 +")); ASSERT_EQUALS("unsigned int", typeOf("unsigned int u1, u2; a = u1 + u2;", "u1 +")); ASSERT_EQUALS("unsigned int", typeOf("unsigned int u1, u2; a = u1 * 2;", "u1 *")); ASSERT_EQUALS("unsigned int", typeOf("unsigned int u1, u2; a = u1 * u2;", "u1 *")); ASSERT_EQUALS("signed int *", typeOf("int x; a = &x;", "&")); ASSERT_EQUALS("signed int *", typeOf("int x; a = &x;", "&")); ASSERT_EQUALS("long double", typeOf("long double x; dostuff(x,1);", "x ,")); ASSERT_EQUALS("long double *", typeOf("long double x; dostuff(&x,1);", "& x ,")); ASSERT_EQUALS("signed int", typeOf("struct X {int i;}; void f(struct X x) { x.i }", ".")); ASSERT_EQUALS("signed int *", typeOf("int *p; a = p++;", "++")); ASSERT_EQUALS("signed int", typeOf("int x; a = x++;", "++")); ASSERT_EQUALS("signed int *", typeOf("enum AB {A,B}; AB *ab; x=ab+2;", "+")); ASSERT_EQUALS("signed int *", typeOf("enum AB {A,B}; enum AB *ab; x=ab+2;", "+")); ASSERT_EQUALS("AB *", typeOf("struct AB {int a; int b;}; AB ab; x=&ab;", "&")); ASSERT_EQUALS("AB *", typeOf("struct AB {int a; int b;}; struct AB ab; x=&ab;", "&")); ASSERT_EQUALS("A::BC *", typeOf("namespace A { struct BC { int b; int c; }; }; struct A::BC abc; x=&abc;", "&")); ASSERT_EQUALS("A::BC *", typeOf("namespace A { struct BC { int b; int c; }; }; struct A::BC *abc; x=abc+1;", "+")); // Unary arithmetic/bit operators ASSERT_EQUALS("signed int", typeOf("int x; a = -x;", "-")); ASSERT_EQUALS("signed int", typeOf("int x; a = ~x;", "~")); ASSERT_EQUALS("double", typeOf("double x; a = -x;", "-")); // Ternary operator ASSERT_EQUALS("signed int", typeOf("int x; a = (b ? x : x);", "?")); ASSERT_EQUALS("", typeOf("int x; a = (b ? x : y);", "?")); ASSERT_EQUALS("double", typeOf("int x; double y; a = (b ? x : y);", "?")); ASSERT_EQUALS("const char *", typeOf("int x; double y; a = (b ? \"a\" : \"b\");", "?")); ASSERT_EQUALS("", typeOf("int x; double y; a = (b ? \"a\" : std::string(\"b\"));", "?")); // Boolean operators ASSERT_EQUALS("bool", typeOf("a > b;", ">")); ASSERT_EQUALS("bool", typeOf(";!b;", "!")); ASSERT_EQUALS("bool", typeOf("c = a && b;", "&&")); // shift => result has same type as lhs ASSERT_EQUALS("signed int", typeOf("int x; a = x << 1U;", "<<")); ASSERT_EQUALS("signed int", typeOf("int x; a = x >> 1U;", ">>")); ASSERT_EQUALS("", typeOf("a = 12 >> x;", ">>", "test.cpp")); // >> might be overloaded ASSERT_EQUALS("signed int", typeOf("a = 12 >> x;", ">>", "test.c")); ASSERT_EQUALS("", typeOf("a = 12 << x;", "<<", "test.cpp")); // << might be overloaded ASSERT_EQUALS("signed int", typeOf("a = 12 << x;", "<<", "test.c")); // assignment => result has same type as lhs ASSERT_EQUALS("unsigned short", typeOf("unsigned short x; x = 3;", "=")); // array.. ASSERT_EQUALS("void * *", typeOf("void * x[10]; a = x + 0;", "+")); ASSERT_EQUALS("signed int *", typeOf("int x[10]; a = x + 1;", "+")); ASSERT_EQUALS("signed int", typeOf("int x[10]; a = x[0] + 1;", "+")); ASSERT_EQUALS("", typeOf("a = x[\"hello\"];", "[", "test.cpp")); ASSERT_EQUALS("const char", typeOf("a = x[\"hello\"];", "[", "test.c")); // cast.. ASSERT_EQUALS("void *", typeOf("a = (void *)0;", "(")); ASSERT_EQUALS("char", typeOf("a = (char)32;", "(")); ASSERT_EQUALS("signed long", typeOf("a = (long)32;", "(")); ASSERT_EQUALS("signed long", typeOf("a = (long int)32;", "(")); ASSERT_EQUALS("signed long long", typeOf("a = (long long)32;", "(")); ASSERT_EQUALS("long double", typeOf("a = (long double)32;", "(")); ASSERT_EQUALS("char", typeOf("a = static_cast(32);", "(")); ASSERT_EQUALS("", typeOf("a = (unsigned x)0;", "(")); // sizeof.. ASSERT_EQUALS("char", typeOf("sizeof(char);", "char")); // const.. ASSERT_EQUALS("const char *", typeOf("a = \"123\";", "\"123\"")); ASSERT_EQUALS("const signed int *", typeOf("const int *a; x = a + 1;", "a +")); ASSERT_EQUALS("signed int * const", typeOf("int * const a; x = a + 1;", "+")); ASSERT_EQUALS("const signed int *", typeOf("const int a[20]; x = a + 1;", "+")); ASSERT_EQUALS("const signed int *", typeOf("const int x; a = &x;", "&")); ASSERT_EQUALS("signed int", typeOf("int * const a; x = *a;", "*")); ASSERT_EQUALS("const signed int", typeOf("const int * const a; x = *a;", "*")); // function call.. ASSERT_EQUALS("signed int", typeOf("int a(int); a(5);", "( 5")); ASSERT_EQUALS("signed int", typeOf("auto a(int) -> int; a(5);", "( 5")); ASSERT_EQUALS("unsigned long", typeOf("sizeof(x);", "(")); ASSERT_EQUALS("signed int", typeOf("int (*a)(int); a(5);", "( 5")); // struct member.. ASSERT_EQUALS("signed int", typeOf("struct AB { int a; int b; } ab; x = ab.a;", ".")); ASSERT_EQUALS("signed int", typeOf("struct AB { int a; int b; } *ab; x = ab[1].a;", ".")); // Overloaded operators ASSERT_EQUALS("Fred", typeOf("class Fred { Fred& operator<(int); }; void f() { Fred fred; x=fred<123; }", "<")); // Static members ASSERT_EQUALS("signed int", typeOf("struct AB { static int a; }; x = AB::a;", "::")); // Pointer to unknown type ASSERT_EQUALS("*", typeOf("Bar* b;", "b")); // Enum ASSERT_EQUALS("char", typeOf("enum E : char { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("signed char", typeOf("enum E : signed char { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("unsigned char", typeOf("enum E : unsigned char { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("signed short", typeOf("enum E : short { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("unsigned short", typeOf("enum E : unsigned short { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("signed int", typeOf("enum E : int { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("unsigned int", typeOf("enum E : unsigned int { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("signed long", typeOf("enum E : long { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("unsigned long", typeOf("enum E : unsigned long { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("signed long long", typeOf("enum E : long long { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); ASSERT_EQUALS("unsigned long long", typeOf("enum E : unsigned long long { }; void foo() { E e[3]; bar(e[0]); }", "[ 0")); // Library types { // PodType Settings settingsWin64; settingsWin64.platformType = Settings::Win64; const Library::PodType u32 = { 4, 'u' }; settingsWin64.library.podtypes["u32"] = u32; settingsWin64.library.podtypes["xyz::x"] = u32; ValueType vt; ASSERT_EQUALS(true, vt.fromLibraryType("u32", &settingsWin64)); ASSERT_EQUALS(true, vt.fromLibraryType("xyz::x", &settingsWin64)); ASSERT_EQUALS(ValueType::Type::INT, vt.type); ASSERT_EQUALS("unsigned int *", typeOf(";void *data = new u32[10];", "new", "test.cpp", &settingsWin64)); ASSERT_EQUALS("unsigned int *", typeOf(";void *data = new xyz::x[10];", "new", "test.cpp", &settingsWin64)); ASSERT_EQUALS("unsigned int", typeOf("; x = (xyz::x)12;", "(", "test.cpp", &settingsWin64)); } { // PlatformType Settings settingsUnix32; settingsUnix32.platformType = Settings::Unix32; Library::PlatformType s32; s32._type = "int"; settingsUnix32.library.platforms[settingsUnix32.platformString()]._platform_types["s32"] = s32; ValueType vt; ASSERT_EQUALS(true, vt.fromLibraryType("s32", &settingsUnix32)); ASSERT_EQUALS(ValueType::Type::INT, vt.type); } { // Container Settings sC; Library::Container c; c.startPattern = "C"; sC.library.containers["C"] = c; ASSERT_EQUALS("container(C) *", typeOf("C*c=new C;","new","test.cpp",&sC)); ASSERT_EQUALS("container(C) *", typeOf("x=(C*)c;","(","test.cpp",&sC)); } // new ASSERT_EQUALS("C *", typeOf("class C {}; x = new C();", "new")); // auto variables ASSERT_EQUALS("signed int", typeOf("; auto x = 3;", "x")); ASSERT_EQUALS("signed int *", typeOf("; auto *p = (int *)0;", "p")); ASSERT_EQUALS("signed int *", typeOf("; auto data = new int[100];", "data")); ASSERT_EQUALS("signed int", typeOf("; auto data = new X::Y; int x=1000; x=x/5;", "/")); // #7970 ASSERT_EQUALS("signed int *", typeOf("; auto data = new (nothrow) int[100];", "data")); ASSERT_EQUALS("signed int *", typeOf("; auto data = new (std::nothrow) int[100];", "data")); ASSERT_EQUALS("const signed short", typeOf("short values[10]; void f() { for (const auto *x : values); }", "x")); ASSERT_EQUALS("signed int *", typeOf("MACRO(test) void test() { auto x = (int*)y; }", "x")); // #7931 (garbage?) // Variable declaration ASSERT_EQUALS("char *", typeOf("; char abc[] = \"abc\";", "[")); ASSERT_EQUALS("", typeOf("; int x[10] = { [3]=1 };", "[ 3 ]")); } void variadic1() { // #7453 { GET_SYMBOL_DB("CBase* create(const char *c1, ...);\n" "int create(COther& ot, const char *c1, ...);\n" "int foo(COther & ot)\n" "{\n" " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" " CBase* cp2 = create(ot, \"AAAA\", 44, (char*)0);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "create ( \"AAAA\""); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); f = Token::findsimplematch(tokenizer.tokens(), "create ( ot"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); } { GET_SYMBOL_DB("int create(COther& ot, const char *c1, ...);\n" "CBase* create(const char *c1, ...);\n" "int foo(COther & ot)\n" "{\n" " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" " CBase* cp2 = create(ot, \"AAAA\", 44, (char*)0);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "create ( \"AAAA\""); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "create ( ot"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); } } void variadic2() { // #7649 { GET_SYMBOL_DB("CBase* create(const char *c1, ...);\n" "CBase* create(const wchar_t *c1, ...);\n" "int foo(COther & ot)\n" "{\n" " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" " CBase* cp2 = create(L\"AAAA\", 44, (char*)0);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "cp1 = create ("); ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 1); f = Token::findsimplematch(tokenizer.tokens(), "cp2 = create ("); ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 2); } { GET_SYMBOL_DB("CBase* create(const wchar_t *c1, ...);\n" "CBase* create(const char *c1, ...);\n" "int foo(COther & ot)\n" "{\n" " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" " CBase* cp2 = create(L\"AAAA\", 44, (char*)0);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "cp1 = create ("); ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "cp2 = create ("); ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 1); } } void variadic3() { // #7387 { GET_SYMBOL_DB("int zdcalc(const XYZ & per, short rs = 0);\n" "double zdcalc(long& length, const XYZ * per);\n" "long mycalc( ) {\n" " long length;\n" " XYZ per;\n" " zdcalc(length, &per);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "zdcalc ( length"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); } { GET_SYMBOL_DB("double zdcalc(long& length, const XYZ * per);\n" "int zdcalc(const XYZ & per, short rs = 0);\n" "long mycalc( ) {\n" " long length;\n" " XYZ per;\n" " zdcalc(length, &per);\n" "}"); const Token *f = Token::findsimplematch(tokenizer.tokens(), "zdcalc ( length"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); } } void noReturnType() { GET_SYMBOL_DB_C("func() { }"); ASSERT(db && db->functionScopes.size() == 1); if (db && db->functionScopes.size() == 1) { ASSERT(db->functionScopes[0]->function != nullptr); if (db->functionScopes[0]->function != nullptr) { const Token *retDef = db->functionScopes[0]->function->retDef; ASSERT_EQUALS("func", retDef ? retDef->str() : ""); } } } void auto1() { GET_SYMBOL_DB("; auto x = \"abc\";"); const Token *autotok = tokenizer.tokens()->next(); ASSERT(autotok && autotok->isStandardType()); const Variable *var = db ? db->getVariableFromVarId(1) : nullptr; ASSERT(var && var->isPointer() && var->isConst()); } void auto2() { GET_SYMBOL_DB("struct S { int i; };\n" "int foo() {\n" " auto a = new S;\n" " auto * b = new S;\n" " auto c = new S[10];\n" " auto * d = new S[10];\n" " return a->i + b->i + c[0]->i + d[0]->i;\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 1 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S" && autotok->type() == nullptr); autotok = Token::findsimplematch(autotok->next(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S" && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 1 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S" && autotok->type() == nullptr); autotok = Token::findsimplematch(autotok->next(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S" && autotok->type() && autotok->type()->name() == "S"); vartok = Token::findsimplematch(tokenizer.tokens(), "a ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "b ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "c ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "d ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(tokenizer.tokens(), "return"); vartok = Token::findsimplematch(vartok, "a"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "b"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "c"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "d"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(tokenizer.tokens(), "return"); vartok = Token::findsimplematch(vartok, "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); } void auto3() { GET_SYMBOL_DB("enum E : unsigned short { A, B, C };\n" "int foo() {\n" " auto a = new E;\n" " auto * b = new E;\n" " auto c = new E[10];\n" " auto * d = new E[10];\n" " return *a + *b + c[0] + d[0];\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 1 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "E" && autotok->type() == nullptr); autotok = Token::findsimplematch(autotok->next(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "E" && autotok->type() && autotok->type()->name() == "E"); autotok = Token::findsimplematch(autotok->next(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 1 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "E" && autotok->type() == nullptr); autotok = Token::findsimplematch(autotok->next(), "auto"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "E" && autotok->type() && autotok->type()->name() == "E"); vartok = Token::findsimplematch(tokenizer.tokens(), "a ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(vartok->next(), "b ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(vartok->next(), "c ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(vartok->next(), "d ="); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(tokenizer.tokens(), "return"); vartok = Token::findsimplematch(vartok, "a"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(vartok->next(), "b"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(vartok->next(), "c"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); vartok = Token::findsimplematch(vartok->next(), "d"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && vartok->variable()->type() && vartok->variable()->type()->name() == "E"); } void auto4() { GET_SYMBOL_DB("struct S { int i; };\n" "int foo() {\n" " S array[10];\n" " for (auto a : array)\n" " a.i = 0;\n" " for (auto & b : array)\n" " b.i = 1;\n" " for (const auto & c : array)\n" " auto ci = c.i;\n" " for (auto * d : array)\n" " d->i = 0;\n" " for (const auto * e : array)\n" " auto ei = e->i;\n" " return array[0].i;\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto a"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto & b"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto & c"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto * d"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto * e"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); vartok = Token::findsimplematch(tokenizer.tokens(), "a :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isReference() && !vartok->variable()->isPointer()); ASSERT(db && vartok && vartok->variable() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "b :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isReference() && !vartok->variable()->isPointer() && !vartok->variable()->isConst()); vartok = Token::findsimplematch(vartok->next(), "c :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isReference() && !vartok->variable()->isPointer() && vartok->variable()->isConst()); vartok = Token::findsimplematch(vartok->next(), "d :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isReference() && vartok->variable()->isPointer() && !vartok->variable()->isConst()); vartok = Token::findsimplematch(vartok->next(), "e :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isReference() && vartok->variable()->isPointer() && vartok->variable()->isConst()); vartok = Token::findsimplematch(tokenizer.tokens(), "a . i"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isPointer() && !vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok, "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "b . i"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isPointer() && vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "c . i"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isPointer() && vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "d . i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && !vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "e . i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && !vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); } void auto5() { GET_SYMBOL_DB("struct S { int i; };\n" "int foo() {\n" " std::vector vec(10);\n" " for (auto a : vec)\n" " a.i = 0;\n" " for (auto & b : vec)\n" " b.i = 0;\n" " for (const auto & c : vec)\n" " auto ci = c.i;\n" " for (auto * d : vec)\n" " d.i = 0;\n" " for (const auto * e : vec)\n" " auto ei = e->i;\n" " return vec[0].i;\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto a"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto & b"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto & c"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto * d"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); autotok = Token::findsimplematch(autotok->next(), "auto * e"); ASSERT(db && autotok && autotok->valueType() && autotok->valueType()->pointer == 0 && autotok->valueType()->constness == 0 && autotok->valueType()->typeScope && autotok->valueType()->typeScope->definedType && autotok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && autotok && autotok->type() && autotok->type()->name() == "S"); vartok = Token::findsimplematch(tokenizer.tokens(), "a :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isReference() && !vartok->variable()->isPointer()); ASSERT(db && vartok && vartok->variable() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "b :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isReference() && !vartok->variable()->isPointer() && !vartok->variable()->isConst()); vartok = Token::findsimplematch(vartok->next(), "c :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isReference() && !vartok->variable()->isPointer() && vartok->variable()->isConst()); vartok = Token::findsimplematch(vartok->next(), "d :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isReference() && vartok->variable()->isPointer() && !vartok->variable()->isConst()); vartok = Token::findsimplematch(vartok->next(), "e :"); ASSERT(db && vartok && vartok->valueType() && vartok->valueType()->typeScope && vartok->valueType()->typeScope->definedType && vartok->valueType()->typeScope->definedType->name() == "S"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isReference() && vartok->variable()->isPointer() && vartok->variable()->isConst()); vartok = Token::findsimplematch(tokenizer.tokens(), "a . i"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isPointer() && !vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok, "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "b . i"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isPointer() && vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "c . i"); ASSERT(db && vartok && vartok->variable() && !vartok->variable()->isPointer() && vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "d . i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && !vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "e . i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->isPointer() && !vartok->variable()->isReference() && vartok->variable()->type() && vartok->variable()->type()->name() == "S"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); vartok = Token::findsimplematch(vartok->next(), "i"); ASSERT(db && vartok && vartok->variable() && vartok->variable()->typeStartToken()->str() == "int"); } void auto6() { // #7963 (segmentation fault) GET_SYMBOL_DB("class WebGLTransformFeedback final\n" ": public nsWrapperCache\n" ", public WebGLRefCountedObject < WebGLTransformFeedback >\n" ", public LinkedListElement < WebGLTransformFeedback >\n" "{\n" "private :\n" "std :: vector < IndexedBufferBinding > mIndexedBindings ;\n" "} ;\n" "struct IndexedBufferBinding\n" "{\n" "IndexedBufferBinding ( ) ;\n" "} ;\n" "const decltype ( WebGLTransformFeedback :: mBuffersForTF ) &\n" "WebGLTransformFeedback :: BuffersForTF ( ) const\n" "{\n" "mBuffersForTF . clear ( ) ;\n" "for ( const auto & cur : mIndexedBindings ) {}\n" "return mBuffersForTF ;\n" "}"); ASSERT_EQUALS(true, db != nullptr); // not null } void auto7() { GET_SYMBOL_DB("struct Foo { int a; int b[10]; };\n" "class Bar {\n" " Foo foo1;\n" " Foo foo2[10];\n" "public:\n" " const Foo & getFoo1() { return foo1; }\n" " const Foo * getFoo2() { return foo2; }\n" "};\n" "int main() {\n" " Bar bar;\n" " auto v1 = bar.getFoo1().a;\n" " auto v2 = bar.getFoo1().b[0];\n" " auto v3 = bar.getFoo1().b;\n" " const auto v4 = bar.getFoo1().b;\n" " const auto * v5 = bar.getFoo1().b;\n" " auto v6 = bar.getFoo2()[0].a;\n" " auto v7 = bar.getFoo2()[0].b[0];\n" " auto v8 = bar.getFoo2()[0].b;\n" " const auto v9 = bar.getFoo2()[0].b;\n" " const auto * v10 = bar.getFoo2()[0].b;\n" " auto v11 = v1 + v2 + v3[0] + v4[0] + v5[0] + v6 + v7 + v8[0] + v9[0] + v10[0];\n" " return v11;\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto v1"); // auto = int, v1 = int ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v1 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { ASSERT_EQUALS(0, vartok->valueType()->constness); ASSERT_EQUALS(0, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int, v2 = int autotok = Token::findsimplematch(autotok, "auto v2"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v2 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { ASSERT_EQUALS(0, vartok->valueType()->constness); ASSERT_EQUALS(0, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = const int *, v3 = const int * (const int[10]) autotok = Token::findsimplematch(autotok, "auto v3"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { TODO_ASSERT_EQUALS(1, 0, autotok->valueType()->constness); ASSERT_EQUALS(1, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v3 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { TODO_ASSERT_EQUALS(1, 0, vartok->valueType()->constness); ASSERT_EQUALS(1, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int *, v4 = const int * (const int[10]) autotok = Token::findsimplematch(autotok, "auto v4"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { TODO_ASSERT_EQUALS(0, 1, autotok->valueType()->constness); ASSERT_EQUALS(1, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v4 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { TODO_ASSERT_EQUALS(1, 0, vartok->valueType()->constness); ASSERT_EQUALS(1, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int, v5 = const int * (const int[10]) autotok = Token::findsimplematch(autotok, "auto * v5"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { TODO_ASSERT_EQUALS(0, 1, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v5 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { TODO_ASSERT_EQUALS(1, 0, vartok->valueType()->constness); ASSERT_EQUALS(1, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int, v6 = int autotok = Token::findsimplematch(autotok, "auto v6"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v6 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { ASSERT_EQUALS(0, vartok->valueType()->constness); ASSERT_EQUALS(0, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int, v7 = int autotok = Token::findsimplematch(autotok, "auto v7"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v7 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { ASSERT_EQUALS(0, vartok->valueType()->constness); ASSERT_EQUALS(0, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = const int *, v8 = const int * (const int[10]) autotok = Token::findsimplematch(autotok, "auto v8"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { TODO_ASSERT_EQUALS(1, 0, autotok->valueType()->constness); ASSERT_EQUALS(1, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v8 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { TODO_ASSERT_EQUALS(1, 0, vartok->valueType()->constness); ASSERT_EQUALS(1, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int *, v9 = const int * (const int[10]) autotok = Token::findsimplematch(autotok, "auto v9"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { TODO_ASSERT_EQUALS(0, 1, autotok->valueType()->constness); ASSERT_EQUALS(1, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v9 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { TODO_ASSERT_EQUALS(1, 0, vartok->valueType()->constness); ASSERT_EQUALS(1, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int, v10 = const int * (const int[10]) autotok = Token::findsimplematch(autotok, "auto * v10"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { TODO_ASSERT_EQUALS(0, 1, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "v10 ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { TODO_ASSERT_EQUALS(1, 0, vartok->valueType()->constness); ASSERT_EQUALS(1, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } // auto = int, v11 = int autotok = Token::findsimplematch(autotok, "auto v11"); TODO_ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, autotok->valueType()->type); } vartok = autotok->next(); TODO_ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { ASSERT_EQUALS(0, vartok->valueType()->constness); ASSERT_EQUALS(0, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, vartok->valueType()->type); } } void auto8() { GET_SYMBOL_DB("std::vector vec;\n" "void foo() {\n" " for (auto it = vec.begin(); it != vec.end(); ++it) { }\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto it"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::UNKNOWN_SIGN, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::ITERATOR, autotok->valueType()->type); } vartok = Token::findsimplematch(autotok, "it ="); ASSERT(db && vartok && vartok->valueType()); if (db && vartok && vartok->valueType()) { ASSERT_EQUALS(0, vartok->valueType()->constness); ASSERT_EQUALS(0, vartok->valueType()->pointer); ASSERT_EQUALS(ValueType::UNKNOWN_SIGN, vartok->valueType()->sign); ASSERT_EQUALS(ValueType::ITERATOR, vartok->valueType()->type); } } void auto9() { // #8044 (segmentation fault) GET_SYMBOL_DB("class DHTTokenTracker {\n" " static const size_t SECRET_SIZE = 4;\n" " unsigned char secret_[2][SECRET_SIZE];\n" " void validateToken();\n" "};\n" "template struct DerefEqual derefEqual(const T& t) {\n" " return DerefEqual(t);\n" "}\n" "template \n" "struct RefLess {\n" " bool operator()(const std::shared_ptr& lhs,\n" " const std::shared_ptr& rhs)\n" " {\n" " return lhs.get() < rhs.get();\n" " }\n" "};\n" "void DHTTokenTracker::validateToken()\n" "{\n" " for (auto& elem : secret_) {\n" " }\n" "}"); ASSERT_EQUALS(true, db != nullptr); // not null } void auto10() { // #8020 GET_SYMBOL_DB("void f() {\n" " std::vector ints(4);\n" " auto iter = ints.begin() + (ints.size() - 1);\n" "}"); const Token *autotok = Token::findsimplematch(tokenizer.tokens(), "auto iter"); ASSERT(db && autotok && autotok->valueType()); if (db && autotok && autotok->valueType()) { ASSERT_EQUALS(0, autotok->valueType()->constness); ASSERT_EQUALS(0, autotok->valueType()->pointer); ASSERT_EQUALS(ValueType::UNKNOWN_SIGN, autotok->valueType()->sign); ASSERT_EQUALS(ValueType::ITERATOR, autotok->valueType()->type); } } void unionWithConstructor() { GET_SYMBOL_DB("union Fred {\n" " Fred(int x) : i(x) { }\n" " Fred(float x) : f(x) { }\n" " int i;\n" " float f;\n" "};"); ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "Fred ( int"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "Fred ( float"); ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); } void using1() { Standards::cppstd_t original_std = settings1.standards.cpp; settings1.standards.cpp = Standards::CPP11; GET_SYMBOL_DB("using INT = int;\n" "using PINT = INT *;\n" "using PCINT = const PINT;\n" "INT i;\n" "PINT pi;\n" "PCINT pci;"); settings1.standards.cpp = original_std; const Token *tok = Token::findsimplematch(tokenizer.tokens(), "INT i ;"); ASSERT(db && tok && tok->next() && tok->next()->valueType()); if (db && tok && tok->next() && tok->next()->valueType()) { tok = tok->next(); ASSERT_EQUALS(0, tok->valueType()->constness); ASSERT_EQUALS(0, tok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, tok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, tok->valueType()->type); } tok = Token::findsimplematch(tokenizer.tokens(), "PINT pi ;"); ASSERT(db && tok && tok->next() && tok->next()->valueType()); if (db && tok && tok->next() && tok->next()->valueType()) { tok = tok->next(); ASSERT_EQUALS(0, tok->valueType()->constness); ASSERT_EQUALS(1, tok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, tok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, tok->valueType()->type); } tok = Token::findsimplematch(tokenizer.tokens(), "PCINT pci ;"); ASSERT(db && tok && tok->next() && tok->next()->valueType()); if (db && tok && tok->next() && tok->next()->valueType()) { tok = tok->next(); ASSERT_EQUALS(1, tok->valueType()->constness); ASSERT_EQUALS(1, tok->valueType()->pointer); ASSERT_EQUALS(ValueType::SIGNED, tok->valueType()->sign); ASSERT_EQUALS(ValueType::INT, tok->valueType()->type); } } void using2() { // #8331 (segmentation fault) Standards::cppstd_t original_std = settings1.standards.cpp; settings1.standards.cpp = Standards::CPP11; { GET_SYMBOL_DB("using pboolean = pboolean;\n" "pboolean b;"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "b ;"); ASSERT(db && tok && !tok->valueType()); } { GET_SYMBOL_DB("using pboolean = bool;\n" "using pboolean = pboolean;\n" "pboolean b;"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "b ;"); ASSERT(db && tok && tok->valueType()); if (db && tok && tok->valueType()) { ASSERT_EQUALS(0, tok->valueType()->constness); ASSERT_EQUALS(0, tok->valueType()->pointer); ASSERT_EQUALS(ValueType::UNKNOWN_SIGN, tok->valueType()->sign); ASSERT_EQUALS(ValueType::BOOL, tok->valueType()->type); } } settings1.standards.cpp = original_std; } }; REGISTER_TEST(TestSymbolDatabase) cppcheck-1.82/test/testthreadexecutor.cpp000066400000000000000000000073711322667425100206550ustar00rootroot00000000000000/* * 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 "settings.h" #include "testsuite.h" #include "threadexecutor.h" #include #include #include #include #include class TestThreadExecutor : public TestFixture { public: TestThreadExecutor() : TestFixture("TestThreadExecutor") { } private: Settings settings; /** * Execute check using n jobs for y files which are have * identical data, given within data. */ void check(unsigned int jobs, int files, int result, const std::string &data) { errout.str(""); output.str(""); if (!ThreadExecutor::isEnabled()) { // Skip this check on systems which don't use this feature return; } std::map filemap; for (int i = 1; i <= files; ++i) { std::ostringstream oss; oss << "file_" << i << ".cpp"; filemap[oss.str()] = 1; } settings.jobs = jobs; ThreadExecutor executor(filemap, settings, *this); for (std::map::const_iterator i = filemap.begin(); i != filemap.end(); ++i) executor.addFileContent(i->first, data); ASSERT_EQUALS(result, executor.check()); } void run() { LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(deadlock_with_many_errors); TEST_CASE(many_threads); TEST_CASE(no_errors_more_files); TEST_CASE(no_errors_less_files); TEST_CASE(no_errors_equal_amount_files); TEST_CASE(one_error_less_files); TEST_CASE(one_error_several_files); } void deadlock_with_many_errors() { std::ostringstream oss; oss << "int main()\n" << "{\n"; for (int i = 0; i < 500; i++) oss << " {char *a = malloc(10);}\n"; oss << " return 0;\n" << "}\n"; check(2, 3, 3, oss.str()); } void many_threads() { check(16, 100, 100, "int main()\n" "{\n" " char *a = malloc(10);\n" " return 0;\n" "}"); } void no_errors_more_files() { check(2, 3, 0, "int main()\n" "{\n" " return 0;\n" "}"); } void no_errors_less_files() { check(2, 1, 0, "int main()\n" "{\n" " return 0;\n" "}"); } void no_errors_equal_amount_files() { check(2, 2, 0, "int main()\n" "{\n" " return 0;\n" "}"); } void one_error_less_files() { check(2, 1, 1, "int main()\n" "{\n" " {char *a = malloc(10);}\n" " return 0;\n" "}"); } void one_error_several_files() { check(2, 20, 20, "int main()\n" "{\n" " {char *a = malloc(10);}\n" " return 0;\n" "}"); } }; REGISTER_TEST(TestThreadExecutor) cppcheck-1.82/test/testtimer.cpp000066400000000000000000000023461322667425100167440ustar00rootroot00000000000000/* * 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 "testsuite.h" #include "timer.h" #include #include class TestTimer : public TestFixture { public: TestTimer() : TestFixture("TestTimer") { } private: void run() { TEST_CASE(result); } void result() const { TimerResultsData t1; t1._clocks = ~(std::clock_t)0; ASSERT(t1.seconds() > 100.0); t1._clocks = CLOCKS_PER_SEC * 5 / 2; ASSERT(std::fabs(t1.seconds()-2.5) < 0.01); } }; REGISTER_TEST(TestTimer) cppcheck-1.82/test/testtoken.cpp000066400000000000000000001116531322667425100167460ustar00rootroot00000000000000/* * 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 "settings.h" #include "testsuite.h" #include "testutils.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include struct InternalError; class TestToken : public TestFixture { public: TestToken() : TestFixture("TestToken") { } private: std::vector arithmeticalOps; std::vector logicalOps; std::vector bitOps; std::vector comparisonOps; std::vector extendedOps; std::vector assignmentOps; void run() { initOps(); TEST_CASE(nextprevious); TEST_CASE(multiCompare); TEST_CASE(multiCompare2); // #3294 - false negative multi compare between "=" and "==" TEST_CASE(multiCompare3); // false positive for %or% on code using "|=" TEST_CASE(multiCompare4); TEST_CASE(multiCompare5); TEST_CASE(getStrLength); TEST_CASE(getStrSize); TEST_CASE(strValue); TEST_CASE(deleteLast); TEST_CASE(nextArgument); TEST_CASE(eraseTokens); TEST_CASE(matchAny); TEST_CASE(matchSingleChar); TEST_CASE(matchNothingOrAnyNotElse); TEST_CASE(matchType); TEST_CASE(matchChar); TEST_CASE(matchCompOp); TEST_CASE(matchStr); TEST_CASE(matchVarid); TEST_CASE(matchNumeric); TEST_CASE(matchBoolean); TEST_CASE(matchOr); TEST_CASE(matchOp); TEST_CASE(matchConstOp); TEST_CASE(isArithmeticalOp); TEST_CASE(isOp); TEST_CASE(isConstOp); TEST_CASE(isExtendedOp); TEST_CASE(isAssignmentOp); TEST_CASE(isStandardType); TEST_CASE(literals); TEST_CASE(operators); TEST_CASE(updateProperties) TEST_CASE(updatePropertiesConcatStr) TEST_CASE(isNameGuarantees1) TEST_CASE(isNameGuarantees2) TEST_CASE(isNameGuarantees3) TEST_CASE(isNameGuarantees4) TEST_CASE(isNameGuarantees5) TEST_CASE(isNameGuarantees6) TEST_CASE(canFindMatchingBracketsNeedsOpen); TEST_CASE(canFindMatchingBracketsInnerPair); TEST_CASE(canFindMatchingBracketsOuterPair); TEST_CASE(canFindMatchingBracketsWithTooManyClosing); TEST_CASE(canFindMatchingBracketsWithTooManyOpening); TEST_CASE(findClosingBracket); TEST_CASE(expressionString); } void nextprevious() const { Token *token = new Token(0); token->str("1"); token->insertToken("2"); token->next()->insertToken("3"); Token *last = token->tokAt(2); ASSERT_EQUALS(token->str(), "1"); ASSERT_EQUALS(token->next()->str(), "2"); ASSERT_EQUALS(token->tokAt(2)->str(), "3"); if (last->next()) ASSERT_EQUALS("Null was expected", ""); ASSERT_EQUALS(last->str(), "3"); ASSERT_EQUALS(last->previous()->str(), "2"); ASSERT_EQUALS(last->tokAt(-2)->str(), "1"); if (token->previous()) ASSERT_EQUALS("Null was expected", ""); TokenList::deleteTokens(token); } bool Match(const std::string &code, const std::string &pattern, unsigned int varid=0) { static const Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(";" + code + ";"); try { tokenizer.tokenize(istr, "test.cpp"); } catch (...) {} return Token::Match(tokenizer.tokens()->next(), pattern.c_str(), varid); } void multiCompare() const { // Test for found Token one(0); one.str("one"); ASSERT_EQUALS(1, Token::multiCompare(&one, "one|two", 0)); Token two(0); two.str("two"); ASSERT_EQUALS(1, Token::multiCompare(&two, "one|two", 0)); ASSERT_EQUALS(1, Token::multiCompare(&two, "verybig|two|", 0)); // Test for empty string found Token notfound(0); notfound.str("notfound"); ASSERT_EQUALS(0, Token::multiCompare(¬found, "one|two|", 0)); // Test for not found ASSERT_EQUALS(static_cast(-1), static_cast(Token::multiCompare(¬found, "one|two", 0))); Token s(0); s.str("s"); ASSERT_EQUALS(static_cast(-1), static_cast(Token::multiCompare(&s, "verybig|two", 0))); Token ne(0); ne.str("ne"); ASSERT_EQUALS(static_cast(-1), static_cast(Token::multiCompare(&ne, "one|two", 0))); Token a(0); a.str("a"); ASSERT_EQUALS(static_cast(-1), static_cast(Token::multiCompare(&a, "abc|def", 0))); Token abcd(0); abcd.str("abcd"); ASSERT_EQUALS(static_cast(-1), static_cast(Token::multiCompare(&abcd, "abc|def", 0))); Token def(0); def.str("default"); ASSERT_EQUALS(static_cast(-1), static_cast(Token::multiCompare(&def, "abc|def", 0))); // %op% Token plus(0); plus.str("+"); ASSERT_EQUALS(1, Token::multiCompare(&plus, "one|%op%", 0)); ASSERT_EQUALS(1, Token::multiCompare(&plus, "%op%|two", 0)); Token x(0); x.str("x"); ASSERT_EQUALS(-1, Token::multiCompare(&x, "one|%op%", 0)); ASSERT_EQUALS(-1, Token::multiCompare(&x, "%op%|two", 0)); } void multiCompare2() const { // #3294 // Original pattern that failed: [[,(=<>+-*|&^] %num% [+-*/] %num% ]|,|)|;|=|%op% givenACodeSampleToTokenize toks("a == 1", true); ASSERT_EQUALS(true, Token::Match(toks.tokens(), "a =|%op%")); } void multiCompare3() const { // Original pattern that failed: "return|(|&&|%oror% %name% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %name% )|&&|%oror%|;" // Code snippet that failed: "return lv@86 |= rv@87 ;" // Note: Also test "reverse" alternative pattern, two different code paths to handle it givenACodeSampleToTokenize toks("return a |= b ;", true); ASSERT_EQUALS(false, Token::Match(toks.tokens(), "return %name% xyz|%or% %name% ;")); ASSERT_EQUALS(false, Token::Match(toks.tokens(), "return %name% %or%|xyz %name% ;")); givenACodeSampleToTokenize toks2("return a | b ;", true); ASSERT_EQUALS(true, Token::Match(toks2.tokens(), "return %name% xyz|%or% %name% ;")); ASSERT_EQUALS(true, Token::Match(toks2.tokens(), "return %name% %or%|xyz %name% ;")); givenACodeSampleToTokenize toks3("return a || b ;", true); ASSERT_EQUALS(false, Token::Match(toks3.tokens(), "return %name% xyz|%or% %name% ;")); ASSERT_EQUALS(false, Token::Match(toks3.tokens(), "return %name% %or%|xyz %name% ;")); ASSERT_EQUALS(true, Token::Match(toks3.tokens(), "return %name% xyz|%oror% %name% ;")); ASSERT_EQUALS(true, Token::Match(toks3.tokens(), "return %name% %oror%|xyz %name% ;")); givenACodeSampleToTokenize toks4("a % b ;", true); ASSERT_EQUALS(true, Token::Match(toks4.tokens(), "%name% >>|<<|&|%or%|^|% %name% ;")); ASSERT_EQUALS(true, Token::Match(toks4.tokens(), "%name% %|>>|<<|&|%or%|^ %name% ;")); ASSERT_EQUALS(true, Token::Match(toks4.tokens(), "%name% >>|<<|&|%or%|%|^ %name% ;")); //%name%|%num% support givenACodeSampleToTokenize num("100", true); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%num%|%name%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%name%|%num%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%name%|%num%|%bool%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%name%|%bool%|%num%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%name%|%bool%|%str%|%num%")); ASSERT_EQUALS(false, Token::Match(num.tokens(), "%bool%|%name%")); ASSERT_EQUALS(false, Token::Match(num.tokens(), "%type%|%bool%|%char%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%type%|%bool%|100")); givenACodeSampleToTokenize numparen("( 100 )", true); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %num%|%name% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %name%|%num% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %name%|%num%|%bool% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %name%|%bool%|%num% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %name%|%bool%|%str%|%num% )|")); ASSERT_EQUALS(false, Token::Match(numparen.tokens(), "(| %bool%|%name% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %num%|%name%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %name%|%num%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %name%|%num%|%bool%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %name%|%bool%|%num%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %name%|%bool%|%str%|%num%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %bool%|%name%| )|")); } void multiCompare4() const { givenACodeSampleToTokenize var("std :: queue < int > foo ;"); ASSERT_EQUALS(Token::eBracket, var.tokens()->tokAt(3)->tokType()); ASSERT_EQUALS(Token::eBracket, var.tokens()->tokAt(5)->tokType()); ASSERT_EQUALS(false, Token::Match(var.tokens(), "std :: queue %op%")); ASSERT_EQUALS(false, Token::Match(var.tokens(), "std :: queue x|%op%")); ASSERT_EQUALS(false, Token::Match(var.tokens(), "std :: queue %op%|x")); } void multiCompare5() const { Token tok(0); tok.str("||"); ASSERT_EQUALS(true, Token::multiCompare(&tok, "+|%or%|%oror%", 0) >= 0); } void getStrLength() const { Token tok(0); tok.str("\"\""); ASSERT_EQUALS(0, (int)Token::getStrLength(&tok)); tok.str("\"test\""); ASSERT_EQUALS(4, (int)Token::getStrLength(&tok)); tok.str("\"test \\\\test\""); ASSERT_EQUALS(10, (int)Token::getStrLength(&tok)); tok.str("\"a\\0\""); ASSERT_EQUALS(1, (int)Token::getStrLength(&tok)); } void getStrSize() const { Token tok(0); tok.str("\"abc\""); ASSERT_EQUALS(sizeof("abc"), Token::getStrSize(&tok)); tok.str("\"\\0abc\""); ASSERT_EQUALS(sizeof("\0abc"), Token::getStrSize(&tok)); tok.str("\"\\\\\""); ASSERT_EQUALS(sizeof("\\"), Token::getStrSize(&tok)); } void strValue() const { Token tok(0); tok.str("\"\""); ASSERT_EQUALS("", tok.strValue()); tok.str("\"0\""); ASSERT_EQUALS("0", tok.strValue()); tok.str("\"a\\n\""); ASSERT_EQUALS("a\n", tok.strValue()); tok.str("\"a\\r\""); ASSERT_EQUALS("a\r", tok.strValue()); tok.str("\"a\\t\""); ASSERT_EQUALS("a\t", tok.strValue()); tok.str("\"\\\\\""); ASSERT_EQUALS("\\", tok.strValue()); tok.str("\"a\\0\""); ASSERT_EQUALS("a", tok.strValue()); } void deleteLast() const { Token *tokensBack = 0; Token tok(&tokensBack); tok.insertToken("aba"); ASSERT_EQUALS(true, tokensBack == tok.next()); tok.deleteNext(); ASSERT_EQUALS(true, tokensBack == &tok); } void nextArgument() const { givenACodeSampleToTokenize example1("foo(1, 2, 3, 4);"); ASSERT_EQUALS(true, Token::simpleMatch(example1.tokens()->tokAt(2)->nextArgument(), "2 , 3")); ASSERT_EQUALS(true, Token::simpleMatch(example1.tokens()->tokAt(4)->nextArgument(), "3 , 4")); givenACodeSampleToTokenize example2("foo();"); ASSERT_EQUALS(true, example2.tokens()->tokAt(2)->nextArgument() == 0); givenACodeSampleToTokenize example3("foo(bar(a, b), 2, 3);"); ASSERT_EQUALS(true, Token::simpleMatch(example3.tokens()->tokAt(2)->nextArgument(), "2 , 3")); givenACodeSampleToTokenize example4("foo(x.i[1], \"\", 3);"); ASSERT_EQUALS(true, Token::simpleMatch(example4.tokens()->tokAt(2)->nextArgument(), "\"\" , 3")); } void eraseTokens() const { givenACodeSampleToTokenize code("begin ; { this code will be removed } end", true); Token::eraseTokens(code.tokens()->next(), code.tokens()->tokAt(9)); ASSERT_EQUALS("begin ; end", code.tokens()->stringifyList(0, false)); } void matchAny() const { givenACodeSampleToTokenize varBitOrVar("abc|def", true); ASSERT_EQUALS(true, Token::Match(varBitOrVar.tokens(), "%name% %or% %name%")); givenACodeSampleToTokenize varLogOrVar("abc||def", true); ASSERT_EQUALS(true, Token::Match(varLogOrVar.tokens(), "%name% %oror% %name%")); } void matchSingleChar() const { givenACodeSampleToTokenize singleChar("a", true); ASSERT_EQUALS(true, Token::Match(singleChar.tokens(), "[a|bc]")); ASSERT_EQUALS(false, Token::Match(singleChar.tokens(), "[d|ef]")); Token multiChar(0); multiChar.str("[ab"); ASSERT_EQUALS(false, Token::Match(&multiChar, "[ab|def]")); } void matchNothingOrAnyNotElse() const { givenACodeSampleToTokenize empty_String("", true); ASSERT_EQUALS(true, Token::Match(empty_String.tokens(), "!!else")); ASSERT_EQUALS(false, Token::Match(empty_String.tokens(), "!!else something")); givenACodeSampleToTokenize ifSemicolon("if ;", true); ASSERT_EQUALS(true, Token::Match(ifSemicolon.tokens(), "if ; !!else")); givenACodeSampleToTokenize ifSemicolonSomething("if ; something", true); ASSERT_EQUALS(true, Token::Match(ifSemicolonSomething.tokens(), "if ; !!else")); givenACodeSampleToTokenize justElse("else", true); ASSERT_EQUALS(false, Token::Match(justElse.tokens(), "!!else")); givenACodeSampleToTokenize ifSemicolonElse("if ; else", true); ASSERT_EQUALS(false, Token::Match(ifSemicolonElse.tokens(), "if ; !!else")); } void matchType() const { givenACodeSampleToTokenize type("abc", true); ASSERT_EQUALS(true, Token::Match(type.tokens(), "%type%")); givenACodeSampleToTokenize isVar("int a = 3 ;"); ASSERT_EQUALS(true, Token::Match(isVar.tokens(), "%type%")); ASSERT_EQUALS(true, Token::Match(isVar.tokens(), "%type% %name%")); ASSERT_EQUALS(false, Token::Match(isVar.tokens(), "%type% %type%")); givenACodeSampleToTokenize noType1_cpp("delete", true, true); ASSERT_EQUALS(false, Token::Match(noType1_cpp.tokens(), "%type%")); givenACodeSampleToTokenize noType1_c("delete", true, false); ASSERT_EQUALS(true, Token::Match(noType1_c.tokens(), "%type%")); givenACodeSampleToTokenize noType2("void delete", true); ASSERT_EQUALS(false, Token::Match(noType2.tokens(), "!!foo %type%")); } void matchChar() const { givenACodeSampleToTokenize chr1("'a'", true); ASSERT_EQUALS(true, Token::Match(chr1.tokens(), "%char%")); givenACodeSampleToTokenize chr2("'1'", true); ASSERT_EQUALS(true, Token::Match(chr2.tokens(), "%char%")); givenACodeSampleToTokenize noChr("\"10\"", true); ASSERT_EQUALS(false, Token::Match(noChr.tokens(), "%char%")); } void matchCompOp() const { givenACodeSampleToTokenize comp1("<=", true); ASSERT_EQUALS(true, Token::Match(comp1.tokens(), "%comp%")); givenACodeSampleToTokenize comp2(">", true); ASSERT_EQUALS(true, Token::Match(comp2.tokens(), "%comp%")); givenACodeSampleToTokenize noComp("=", true); ASSERT_EQUALS(false, Token::Match(noComp.tokens(), "%comp%")); } void matchStr() const { givenACodeSampleToTokenize noStr1("abc", true); ASSERT_EQUALS(false, Token::Match(noStr1.tokens(), "%str%")); givenACodeSampleToTokenize noStr2("'a'", true); ASSERT_EQUALS(false, Token::Match(noStr2.tokens(), "%str%")); givenACodeSampleToTokenize str("\"abc\"", true); ASSERT_EQUALS(true, Token::Match(str.tokens(), "%str%")); // Empty string givenACodeSampleToTokenize emptyStr("\"\"", true); ASSERT_EQUALS(true, Token::Match(emptyStr.tokens(), "%str%")); } void matchVarid() const { givenACodeSampleToTokenize var("int a ; int b ;"); // Varid == 0 should throw exception ASSERT_THROW(Token::Match(var.tokens(), "%type% %varid% ; %type% %name%", 0),InternalError); ASSERT_EQUALS(true, Token::Match(var.tokens(), "%type% %varid% ; %type% %name%", 1)); ASSERT_EQUALS(true, Token::Match(var.tokens(), "%type% %name% ; %type% %varid%", 2)); // Try to match two different varids in one match call ASSERT_EQUALS(false, Token::Match(var.tokens(), "%type% %varid% ; %type% %varid%", 2)); // %var% matches with every varid other than 0 ASSERT_EQUALS(true, Token::Match(var.tokens(), "%type% %var% ;")); ASSERT_EQUALS(false, Token::Match(var.tokens(), "%var% %var% ;")); } void matchNumeric() const { givenACodeSampleToTokenize nonNumeric("abc", true); ASSERT_EQUALS(false, Token::Match(nonNumeric.tokens(), "%num%")); givenACodeSampleToTokenize binary("101010b", true); ASSERT_EQUALS(true, Token::Match(binary.tokens(), "%num%")); givenACodeSampleToTokenize octal("0123", true); ASSERT_EQUALS(true, Token::Match(octal.tokens(), "%num%")); givenACodeSampleToTokenize decimal("4567", true); ASSERT_EQUALS(true, Token::Match(decimal.tokens(), "%num%")); givenACodeSampleToTokenize hexadecimal("0xDEADBEEF", true); ASSERT_EQUALS(true, Token::Match(hexadecimal.tokens(), "%num%")); givenACodeSampleToTokenize floatingPoint("0.0f", true); ASSERT_EQUALS(true, Token::Match(floatingPoint.tokens(), "%num%")); givenACodeSampleToTokenize doublePrecision("0.0d", true); ASSERT_EQUALS(true, Token::Match(doublePrecision.tokens(), "%num%")); givenACodeSampleToTokenize signedLong("0L", true); ASSERT_EQUALS(true, Token::Match(signedLong.tokens(), "%num%")); givenACodeSampleToTokenize negativeSignedLong("-0L", true); ASSERT_EQUALS(true, Token::Match(negativeSignedLong.tokens(), "- %num%")); givenACodeSampleToTokenize positiveSignedLong("+0L", true); ASSERT_EQUALS(true, Token::Match(positiveSignedLong.tokens(), "+ %num%")); givenACodeSampleToTokenize unsignedInt("0U", true); ASSERT_EQUALS(true, Token::Match(unsignedInt.tokens(), "%num%")); givenACodeSampleToTokenize unsignedLong("0UL", true); ASSERT_EQUALS(true, Token::Match(unsignedLong.tokens(), "%num%")); givenACodeSampleToTokenize unsignedLongLong("0ULL", true); ASSERT_EQUALS(true, Token::Match(unsignedLongLong.tokens(), "%num%")); givenACodeSampleToTokenize positive("+666", true); ASSERT_EQUALS(true, Token::Match(positive.tokens(), "+ %num%")); givenACodeSampleToTokenize negative("-42", true); ASSERT_EQUALS(true, Token::Match(negative.tokens(), "- %num%")); givenACodeSampleToTokenize negativeNull("-.0", true); ASSERT_EQUALS(true, Token::Match(negativeNull.tokens(), "- %num%")); givenACodeSampleToTokenize positiveNull("+.0", true); ASSERT_EQUALS(true, Token::Match(positiveNull.tokens(), "+ %num%")); } void matchBoolean() const { givenACodeSampleToTokenize yes("YES", true); ASSERT_EQUALS(false, Token::Match(yes.tokens(), "%bool%")); givenACodeSampleToTokenize positive("true", true); ASSERT_EQUALS(true, Token::Match(positive.tokens(), "%bool%")); givenACodeSampleToTokenize negative("false", true); ASSERT_EQUALS(true, Token::Match(negative.tokens(), "%bool%")); } void matchOr() const { givenACodeSampleToTokenize bitwiseOr(";|;", true); ASSERT_EQUALS(true, Token::Match(bitwiseOr.tokens(), "; %or%")); ASSERT_EQUALS(true, Token::Match(bitwiseOr.tokens(), "; %op%")); ASSERT_EQUALS(false, Token::Match(bitwiseOr.tokens(), "; %oror%")); givenACodeSampleToTokenize bitwiseOrAssignment(";|=;"); ASSERT_EQUALS(false, Token::Match(bitwiseOrAssignment.tokens(), "; %or%")); ASSERT_EQUALS(true, Token::Match(bitwiseOrAssignment.tokens(), "; %op%")); ASSERT_EQUALS(false, Token::Match(bitwiseOrAssignment.tokens(), "; %oror%")); givenACodeSampleToTokenize logicalOr(";||;", true); ASSERT_EQUALS(false, Token::Match(logicalOr.tokens(), "; %or%")); ASSERT_EQUALS(true, Token::Match(logicalOr.tokens(), "; %op%")); ASSERT_EQUALS(true, Token::Match(logicalOr.tokens(), "; %oror%")); ASSERT_EQUALS(true, Token::Match(logicalOr.tokens(), "; &&|%oror%")); ASSERT_EQUALS(true, Token::Match(logicalOr.tokens(), "; %oror%|&&")); givenACodeSampleToTokenize logicalAnd(";&&;", true); ASSERT_EQUALS(true, Token::simpleMatch(logicalAnd.tokens(), "; &&")); ASSERT_EQUALS(true, Token::Match(logicalAnd.tokens(), "; &&|%oror%")); ASSERT_EQUALS(true, Token::Match(logicalAnd.tokens(), "; %oror%|&&")); } static void append_vector(std::vector &dest, const std::vector &src) { dest.insert(dest.end(), src.begin(), src.end()); } void initOps() { arithmeticalOps.push_back("+"); arithmeticalOps.push_back("-"); arithmeticalOps.push_back("*"); arithmeticalOps.push_back("/"); arithmeticalOps.push_back("%"); arithmeticalOps.push_back("<<"); arithmeticalOps.push_back(">>"); logicalOps.push_back("&&"); logicalOps.push_back("||"); logicalOps.push_back("!"); comparisonOps.push_back("=="); comparisonOps.push_back("!="); comparisonOps.push_back("<"); comparisonOps.push_back("<="); comparisonOps.push_back(">"); comparisonOps.push_back(">="); bitOps.push_back("&"); bitOps.push_back("|"); bitOps.push_back("^"); bitOps.push_back("~"); extendedOps.push_back(","); extendedOps.push_back("["); extendedOps.push_back("]"); extendedOps.push_back("("); extendedOps.push_back(")"); extendedOps.push_back("?"); extendedOps.push_back(":"); assignmentOps.push_back("="); assignmentOps.push_back("+="); assignmentOps.push_back("-="); assignmentOps.push_back("*="); assignmentOps.push_back("/="); assignmentOps.push_back("%="); assignmentOps.push_back("&="); assignmentOps.push_back("^="); assignmentOps.push_back("|="); assignmentOps.push_back("<<="); assignmentOps.push_back(">>="); } void matchOp() { std::vector test_ops; append_vector(test_ops, arithmeticalOps); append_vector(test_ops, bitOps); append_vector(test_ops, comparisonOps); append_vector(test_ops, logicalOps); append_vector(test_ops, assignmentOps); std::vector::const_iterator test_op, test_ops_end = test_ops.end(); for (test_op = test_ops.begin(); test_op != test_ops_end; ++test_op) { ASSERT_EQUALS(true, Match(*test_op, "%op%")); } // Negative test against other operators std::vector other_ops; append_vector(other_ops, extendedOps); std::vector::const_iterator other_op, other_ops_end = other_ops.end(); for (other_op = other_ops.begin(); other_op != other_ops_end; ++other_op) { ASSERT_EQUALS_MSG(false, Match(*other_op, "%op%"), "Failing other operator: " + *other_op); } } void matchConstOp() { std::vector test_ops; append_vector(test_ops, arithmeticalOps); append_vector(test_ops, bitOps); append_vector(test_ops, comparisonOps); append_vector(test_ops, logicalOps); std::vector::const_iterator test_op, test_ops_end = test_ops.end(); for (test_op = test_ops.begin(); test_op != test_ops_end; ++test_op) { ASSERT_EQUALS(true, Match(*test_op, "%cop%")); } // Negative test against other operators std::vector other_ops; append_vector(other_ops, extendedOps); append_vector(other_ops, assignmentOps); std::vector::const_iterator other_op, other_ops_end = other_ops.end(); for (other_op = other_ops.begin(); other_op != other_ops_end; ++other_op) { ASSERT_EQUALS_MSG(false, Match(*other_op, "%cop%"), "Failing other operator: " + *other_op); } } void isArithmeticalOp() const { std::vector::const_iterator test_op, test_ops_end = arithmeticalOps.end(); for (test_op = arithmeticalOps.begin(); test_op != test_ops_end; ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(true, tok.isArithmeticalOp()); } // Negative test against other operators std::vector other_ops; append_vector(other_ops, bitOps); append_vector(other_ops, comparisonOps); append_vector(other_ops, logicalOps); append_vector(other_ops, extendedOps); append_vector(other_ops, assignmentOps); std::vector::const_iterator other_op, other_ops_end = other_ops.end(); for (other_op = other_ops.begin(); other_op != other_ops_end; ++other_op) { Token tok(nullptr); tok.str(*other_op); ASSERT_EQUALS_MSG(false, tok.isArithmeticalOp(), "Failing arithmetical operator: " + *other_op); } } void isOp() const { std::vector test_ops; append_vector(test_ops, arithmeticalOps); append_vector(test_ops, bitOps); append_vector(test_ops, comparisonOps); append_vector(test_ops, logicalOps); append_vector(test_ops, assignmentOps); std::vector::const_iterator test_op, test_ops_end = test_ops.end(); for (test_op = test_ops.begin(); test_op != test_ops_end; ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(true, tok.isOp()); } // Negative test against other operators std::vector other_ops; append_vector(other_ops, extendedOps); std::vector::const_iterator other_op, other_ops_end = other_ops.end(); for (other_op = other_ops.begin(); other_op != other_ops_end; ++other_op) { Token tok(nullptr); tok.str(*other_op); ASSERT_EQUALS_MSG(false, tok.isOp(), "Failing normal operator: " + *other_op); } } void isConstOp() const { std::vector test_ops; append_vector(test_ops, arithmeticalOps); append_vector(test_ops, bitOps); append_vector(test_ops, comparisonOps); append_vector(test_ops, logicalOps); std::vector::const_iterator test_op, test_ops_end = test_ops.end(); for (test_op = test_ops.begin(); test_op != test_ops_end; ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(true, tok.isConstOp()); } // Negative test against other operators std::vector other_ops; append_vector(other_ops, extendedOps); append_vector(other_ops, assignmentOps); std::vector::const_iterator other_op, other_ops_end = other_ops.end(); for (other_op = other_ops.begin(); other_op != other_ops_end; ++other_op) { Token tok(nullptr); tok.str(*other_op); ASSERT_EQUALS_MSG(false, tok.isConstOp(), "Failing normal operator: " + *other_op); } } void isExtendedOp() const { std::vector test_ops; append_vector(test_ops, arithmeticalOps); append_vector(test_ops, bitOps); append_vector(test_ops, comparisonOps); append_vector(test_ops, logicalOps); append_vector(test_ops, extendedOps); std::vector::const_iterator test_op, test_ops_end = test_ops.end(); for (test_op = test_ops.begin(); test_op != test_ops_end; ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(true, tok.isExtendedOp()); } // Negative test against assignment operators std::vector::const_iterator other_op, other_ops_end = assignmentOps.end(); for (other_op = assignmentOps.begin(); other_op != other_ops_end; ++other_op) { Token tok(nullptr); tok.str(*other_op); ASSERT_EQUALS_MSG(false, tok.isExtendedOp(), "Failing assignment operator: " + *other_op); } } void isAssignmentOp() const { std::vector::const_iterator test_op, test_ops_end = assignmentOps.end(); for (test_op = assignmentOps.begin(); test_op != test_ops_end; ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(true, tok.isAssignmentOp()); } // Negative test against other operators std::vector other_ops; append_vector(other_ops, arithmeticalOps); append_vector(other_ops, bitOps); append_vector(other_ops, comparisonOps); append_vector(other_ops, logicalOps); append_vector(other_ops, extendedOps); std::vector::const_iterator other_op, other_ops_end = other_ops.end(); for (other_op = other_ops.begin(); other_op != other_ops_end; ++other_op) { Token tok(nullptr); tok.str(*other_op); ASSERT_EQUALS_MSG(false, tok.isAssignmentOp(), "Failing assignment operator: " + *other_op); } } void operators() const { std::vector::const_iterator test_op; for (test_op = extendedOps.begin(); test_op != extendedOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eExtendedOp, tok.tokType()); } for (test_op = logicalOps.begin(); test_op != logicalOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eLogicalOp, tok.tokType()); } for (test_op = bitOps.begin(); test_op != bitOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eBitOp, tok.tokType()); } for (test_op = comparisonOps.begin(); test_op != comparisonOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eComparisonOp, tok.tokType()); } Token tok(nullptr); tok.str("++"); ASSERT_EQUALS(Token::eIncDecOp, tok.tokType()); tok.str("--"); ASSERT_EQUALS(Token::eIncDecOp, tok.tokType()); } void literals() const { Token tok(nullptr); tok.str("\"foo\""); ASSERT(tok.tokType() == Token::eString); tok.str("\"\""); ASSERT(tok.tokType() == Token::eString); tok.str("'f'"); ASSERT(tok.tokType() == Token::eChar); tok.str("12345"); ASSERT(tok.tokType() == Token::eNumber); tok.str("-55"); ASSERT(tok.tokType() == Token::eNumber); tok.str("true"); ASSERT(tok.tokType() == Token::eBoolean); tok.str("false"); ASSERT(tok.tokType() == Token::eBoolean); } void isStandardType() const { std::vector standard_types; standard_types.push_back("bool"); standard_types.push_back("char"); standard_types.push_back("short"); standard_types.push_back("int"); standard_types.push_back("long"); standard_types.push_back("float"); standard_types.push_back("double"); standard_types.push_back("size_t"); std::vector::const_iterator test_op, test_ops_end = standard_types.end(); for (test_op = standard_types.begin(); test_op != test_ops_end; ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS_MSG(true, tok.isStandardType(), "Failing standard type: " + *test_op); } // Negative test Token tok(nullptr); tok.str("string"); ASSERT_EQUALS(false, tok.isStandardType()); // Change back to standard type tok.str("int"); ASSERT_EQUALS(true, tok.isStandardType()); // token can't be both type and variable tok.str("abc"); tok.isStandardType(true); tok.varId(123); ASSERT_EQUALS(false, tok.isStandardType()); } void updateProperties() const { Token tok(nullptr); tok.str("foobar"); ASSERT_EQUALS(true, tok.isName()); ASSERT_EQUALS(false, tok.isNumber()); tok.str("123456"); ASSERT_EQUALS(false, tok.isName()); ASSERT_EQUALS(true, tok.isNumber()); } void updatePropertiesConcatStr() const { Token tok(nullptr); tok.str("true"); ASSERT_EQUALS(true, tok.isBoolean()); tok.concatStr("123"); ASSERT_EQUALS(false, tok.isBoolean()); ASSERT_EQUALS("tru23", tok.str()); } void isNameGuarantees1() const { Token tok(nullptr); tok.str("Name"); ASSERT_EQUALS(true, tok.isName()); } void isNameGuarantees2() const { Token tok(nullptr); tok.str("_name"); ASSERT_EQUALS(true, tok.isName()); } void isNameGuarantees3() const { Token tok(nullptr); tok.str("_123"); ASSERT_EQUALS(true, tok.isName()); } void isNameGuarantees4() const { Token tok(nullptr); tok.str("123456"); ASSERT_EQUALS(false, tok.isName()); ASSERT_EQUALS(true, tok.isNumber()); } void isNameGuarantees5() const { Token tok(nullptr); tok.str("a123456"); ASSERT_EQUALS(true, tok.isName()); ASSERT_EQUALS(false, tok.isNumber()); } void isNameGuarantees6() const { Token tok(nullptr); tok.str("$f"); ASSERT_EQUALS(true, tok.isName()); } void canFindMatchingBracketsNeedsOpen() const { givenACodeSampleToTokenize var("std::deque > intsets;"); const Token* t = var.tokens()->findClosingBracket(); ASSERT(t == nullptr); } void canFindMatchingBracketsInnerPair() const { givenACodeSampleToTokenize var("std::deque > intsets;"); Token* t = const_cast(var.tokens()->tokAt(7))->findClosingBracket(); ASSERT_EQUALS(">", t->str()); ASSERT(var.tokens()->tokAt(9) == t); } void canFindMatchingBracketsOuterPair() const { givenACodeSampleToTokenize var("std::deque > intsets;"); const Token* t = var.tokens()->tokAt(3)->findClosingBracket(); ASSERT_EQUALS(">", t->str()); ASSERT(var.tokens()->tokAt(10) == t); } void canFindMatchingBracketsWithTooManyClosing() const { givenACodeSampleToTokenize var("X< 1>2 > x1;\n"); const Token* t = var.tokens()->next()->findClosingBracket(); ASSERT_EQUALS(">", t->str()); ASSERT(var.tokens()->tokAt(3) == t); } void canFindMatchingBracketsWithTooManyOpening() const { givenACodeSampleToTokenize var("X < (2 < 1) > x1;\n"); const Token* t = var.tokens()->next()->findClosingBracket(); ASSERT(t != nullptr && t->str() == ">"); t = var.tokens()->tokAt(4)->findClosingBracket(); ASSERT(t == nullptr); } void findClosingBracket() { givenACodeSampleToTokenize var("template struct S : public Fred> {}"); const Token* t = var.tokens()->next()->findClosingBracket(); ASSERT(Token::simpleMatch(t, "> struct")); } void expressionString() { givenACodeSampleToTokenize var1("void f() { *((unsigned long long *)x) = 0; }"); const Token *tok1 = Token::findsimplematch(var1.tokens(), "*"); ASSERT_EQUALS("*((unsigned long long*)x)", tok1->expressionString()); givenACodeSampleToTokenize var2("typedef unsigned long long u64; void f() { *((u64 *)x) = 0; }"); const Token *tok2 = Token::findsimplematch(var2.tokens(), "*"); ASSERT_EQUALS("*((unsigned long long*)x)", tok2->expressionString()); } }; REGISTER_TEST(TestToken) cppcheck-1.82/test/testtokenize.cpp000077500000000000000000014322211322667425100174570ustar00rootroot00000000000000/* * 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 "config.h" #include "platform.h" #include "preprocessor.h" // usually tests here should not use preprocessor... #include "settings.h" #include "standards.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include struct InternalError; class TestTokenizer : public TestFixture { public: TestTokenizer() : TestFixture("TestTokenizer") { } private: Settings settings0; Settings settings1; Settings settings2; Settings settings_windows; void run() { LOAD_LIB_2(settings_windows.library, "windows.cfg"); TEST_CASE(tokenize1); TEST_CASE(tokenize2); TEST_CASE(tokenize3); TEST_CASE(tokenize4); TEST_CASE(tokenize5); TEST_CASE(tokenize6); // array access. replace "*(p+1)" => "p[1]" TEST_CASE(tokenize7); TEST_CASE(tokenize8); TEST_CASE(tokenize9); TEST_CASE(tokenize11); TEST_CASE(tokenize13); // bailout if the code contains "@" - that is not handled well. TEST_CASE(tokenize14); // tokenize "0X10" => 16 TEST_CASE(tokenizeHexWithSuffix); // tokenize 0xFFFFFFul TEST_CASE(tokenize15); // tokenize ".123" TEST_CASE(tokenize17); // #2759 TEST_CASE(tokenize18); // tokenize "(X&&Y)" into "( X && Y )" instead of "( X & & Y )" TEST_CASE(tokenize19); // #3006 (segmentation fault) TEST_CASE(tokenize21); // tokenize 0x0E-7 TEST_CASE(tokenize22); // special marker $ from preprocessor TEST_CASE(tokenize25); // #4239 (segmentation fault) TEST_CASE(tokenize26); // #4245 (segmentation fault) TEST_CASE(tokenize27); // #4525 (segmentation fault) TEST_CASE(tokenize31); // #3503 (Wrong handling of member function taking function pointer as argument) TEST_CASE(tokenize32); // #5884 (fsanitize=undefined: left shift of negative value -10000 in lib/templatesimplifier.cpp:852:46) TEST_CASE(tokenize33); // #5780 Various crashes on valid template code TEST_CASE(tokenize34); // #8031 TEST_CASE(validate); TEST_CASE(syntax_case_default); TEST_CASE(foreach); // #3690 TEST_CASE(combineOperators); TEST_CASE(concatenateNegativeNumber); TEST_CASE(longtok); TEST_CASE(simplifyCasts1); TEST_CASE(simplifyCasts2); TEST_CASE(simplifyCasts3); TEST_CASE(simplifyCasts4); TEST_CASE(simplifyCasts5); TEST_CASE(simplifyCasts7); TEST_CASE(simplifyCasts8); TEST_CASE(simplifyCasts9); TEST_CASE(simplifyCasts10); TEST_CASE(simplifyCasts11); TEST_CASE(simplifyCasts12); TEST_CASE(simplifyCasts13); TEST_CASE(simplifyCasts14); TEST_CASE(simplifyCasts15); // #5996 - don't remove cast in 'a+static_cast(b?60:0)' TEST_CASE(simplifyCasts16); // #6278 TEST_CASE(simplifyCasts17); // #6110 - don't remove any parentheses in 'a(b)(c)' TEST_CASE(inlineasm); TEST_CASE(simplifyAsm2); // #4725 (writing asm() around "^{}") TEST_CASE(ifAddBraces1); TEST_CASE(ifAddBraces2); TEST_CASE(ifAddBraces3); TEST_CASE(ifAddBraces4); TEST_CASE(ifAddBraces5); TEST_CASE(ifAddBraces7); TEST_CASE(ifAddBraces9); TEST_CASE(ifAddBraces10); TEST_CASE(ifAddBraces11); TEST_CASE(ifAddBraces12); TEST_CASE(ifAddBraces13); TEST_CASE(ifAddBraces15); // #2616 - unknown macro before if TEST_CASE(ifAddBraces16); TEST_CASE(ifAddBraces17); // '} else' should be in the same line TEST_CASE(ifAddBraces18); // #3424 - if if { } else else TEST_CASE(ifAddBraces19); // #3928 - if for if else TEST_CASE(ifAddBraces20); // #5012 - syntax error 'else }' TEST_CASE(ifAddBraces21); // #5332 - if (x) label: {} .. TEST_CASE(whileAddBraces); TEST_CASE(doWhileAddBraces); TEST_CASE(forAddBraces1); TEST_CASE(forAddBraces2); // #5088 TEST_CASE(simplifyKnownVariables1); TEST_CASE(simplifyKnownVariables2); TEST_CASE(simplifyKnownVariables3); TEST_CASE(simplifyKnownVariables4); TEST_CASE(simplifyKnownVariables5); TEST_CASE(simplifyKnownVariables6); TEST_CASE(simplifyKnownVariables7); TEST_CASE(simplifyKnownVariables8); TEST_CASE(simplifyKnownVariables9); TEST_CASE(simplifyKnownVariables10); TEST_CASE(simplifyKnownVariables11); TEST_CASE(simplifyKnownVariables12); TEST_CASE(simplifyKnownVariables13); TEST_CASE(simplifyKnownVariables14); TEST_CASE(simplifyKnownVariables15); TEST_CASE(simplifyKnownVariables16); TEST_CASE(simplifyKnownVariables17); TEST_CASE(simplifyKnownVariables18); TEST_CASE(simplifyKnownVariables19); TEST_CASE(simplifyKnownVariables20); TEST_CASE(simplifyKnownVariables21); TEST_CASE(simplifyKnownVariables22); TEST_CASE(simplifyKnownVariables23); TEST_CASE(simplifyKnownVariables25); TEST_CASE(simplifyKnownVariables27); TEST_CASE(simplifyKnownVariables28); TEST_CASE(simplifyKnownVariables29); // ticket #1811 TEST_CASE(simplifyKnownVariables30); TEST_CASE(simplifyKnownVariables31); TEST_CASE(simplifyKnownVariables32); // const TEST_CASE(simplifyKnownVariables33); // struct variable TEST_CASE(simplifyKnownVariables34); TEST_CASE(simplifyKnownVariables35); // ticket #2353 - False positive: Division by zero 'if (x == 0) return 0; return 10 / x;' TEST_CASE(simplifyKnownVariables36); // ticket #2304 - known value for strcpy parameter TEST_CASE(simplifyKnownVariables37); // ticket #2398 - false positive caused by no simplification in for loop TEST_CASE(simplifyKnownVariables38); // ticket #2399 - simplify conditions TEST_CASE(simplifyKnownVariables39); TEST_CASE(simplifyKnownVariables40); TEST_CASE(simplifyKnownVariables41); // p=&x; if (p) .. TEST_CASE(simplifyKnownVariables42); // ticket #2031 - known string value after strcpy TEST_CASE(simplifyKnownVariables43); TEST_CASE(simplifyKnownVariables44); // ticket #3117 - don't simplify static variables TEST_CASE(simplifyKnownVariables45); // ticket #3281 - static constant variable not simplified TEST_CASE(simplifyKnownVariables46); // ticket #3587 - >> TEST_CASE(simplifyKnownVariables47); // ticket #3627 - >> TEST_CASE(simplifyKnownVariables48); // ticket #3754 - wrong simplification in for loop header TEST_CASE(simplifyKnownVariables49); // #3691 - continue in switch TEST_CASE(simplifyKnownVariables50); // #4066 sprintf changes TEST_CASE(simplifyKnownVariables51); // #4409 hang TEST_CASE(simplifyKnownVariables52); // #4728 "= x %cop%" TEST_CASE(simplifyKnownVariables53); // references TEST_CASE(simplifyKnownVariables54); // #4913 'x' is not 0 after *--x=0; TEST_CASE(simplifyKnownVariables55); // pointer alias TEST_CASE(simplifyKnownVariables56); // ticket #5301 - >> TEST_CASE(simplifyKnownVariables57); // ticket #4724 TEST_CASE(simplifyKnownVariables58); // ticket #5268 TEST_CASE(simplifyKnownVariables59); // skip for header TEST_CASE(simplifyKnownVariables60); // #6829 TEST_CASE(simplifyKnownVariables61); // #7805 TEST_CASE(simplifyKnownVariables62); // #5666 - p=&str[0] TEST_CASE(simplifyKnownVariablesBailOutAssign1); TEST_CASE(simplifyKnownVariablesBailOutAssign2); TEST_CASE(simplifyKnownVariablesBailOutAssign3); // #4395 - nested assignments TEST_CASE(simplifyKnownVariablesBailOutFor1); TEST_CASE(simplifyKnownVariablesBailOutFor2); TEST_CASE(simplifyKnownVariablesBailOutFor3); TEST_CASE(simplifyKnownVariablesBailOutMemberFunction); TEST_CASE(simplifyKnownVariablesBailOutConditionalIncrement); TEST_CASE(simplifyKnownVariablesBailOutSwitchBreak); // ticket #2324 TEST_CASE(simplifyKnownVariablesFloat); // #2454 - float variable TEST_CASE(simplifyKnownVariablesClassMember); // #2815 - value of class member may be changed by function call TEST_CASE(simplifyKnownVariablesFunctionCalls); // Function calls (don't assume pass by reference) TEST_CASE(simplifyKnownVariablesGlobalVars); TEST_CASE(simplifyKnownVariablesReturn); // 3500 - return TEST_CASE(simplifyKnownVariablesPointerAliasFunctionCall); // #7440 TEST_CASE(simplifyExternC); TEST_CASE(simplifyKeyword); // #5842 - remove C99 static keyword between [] TEST_CASE(isZeroNumber); TEST_CASE(isOneNumber); TEST_CASE(isTwoNumber); TEST_CASE(simplifyFunctionParameters); TEST_CASE(simplifyFunctionParameters1); // #3721 TEST_CASE(simplifyFunctionParameters2); // #4430 TEST_CASE(simplifyFunctionParameters3); // #4436 TEST_CASE(simplifyFunctionParametersErrors); TEST_CASE(removeParentheses1); // Ticket #61 TEST_CASE(removeParentheses3); TEST_CASE(removeParentheses4); // Ticket #390 TEST_CASE(removeParentheses5); // Ticket #392 TEST_CASE(removeParentheses6); TEST_CASE(removeParentheses7); TEST_CASE(removeParentheses8); // Ticket #1865 TEST_CASE(removeParentheses9); // Ticket #1962 TEST_CASE(removeParentheses10); // Ticket #2320 TEST_CASE(removeParentheses11); // Ticket #2505 TEST_CASE(removeParentheses12); // Ticket #2760 ',(b)=' TEST_CASE(removeParentheses13); TEST_CASE(removeParentheses14); // Ticket #3309 TEST_CASE(removeParentheses15); // Ticket #4142 TEST_CASE(removeParentheses16); // Ticket #4423 '*(x.y)=' TEST_CASE(removeParentheses17); // Don't remove parentheses in 'a ? b : (c>0 ? d : e);' TEST_CASE(removeParentheses18); // 'float(*a)[2]' => 'float *a[2]' TEST_CASE(removeParentheses19); // ((typeof(x) *)0) TEST_CASE(removeParentheses20); // Ticket #5479: a>(2); TEST_CASE(removeParentheses21); // Don't "simplify" casts TEST_CASE(removeParentheses22); TEST_CASE(removeParentheses23); // Ticket #6103 - Infinite loop upon valid input TEST_CASE(removeParentheses24); // Ticket #7040 TEST_CASE(tokenize_double); TEST_CASE(tokenize_strings); TEST_CASE(simplify_constants); TEST_CASE(simplify_constants2); TEST_CASE(simplify_constants3); TEST_CASE(simplify_constants4); TEST_CASE(simplify_constants5); TEST_CASE(simplify_constants6); // Ticket #5625: Ternary operator as template parameter TEST_CASE(simplifyMulAndParens); // Ticket #2784 + #3184 TEST_CASE(simplifyStructDecl); TEST_CASE(vardecl1); TEST_CASE(vardecl2); TEST_CASE(vardecl3); TEST_CASE(vardecl4); TEST_CASE(vardecl5); // #7048 TEST_CASE(vardec_static); TEST_CASE(vardecl6); TEST_CASE(vardecl7); TEST_CASE(vardecl8); TEST_CASE(vardecl9); TEST_CASE(vardecl10); TEST_CASE(vardecl11); TEST_CASE(vardecl12); TEST_CASE(vardecl13); TEST_CASE(vardecl14); TEST_CASE(vardecl15); TEST_CASE(vardecl16); TEST_CASE(vardecl17); TEST_CASE(vardecl18); TEST_CASE(vardecl19); TEST_CASE(vardecl20); // #3700 - register const int H = 0; TEST_CASE(vardecl21); // #4042 - a::b const *p = 0; TEST_CASE(vardecl22); // #4211 - segmentation fault TEST_CASE(vardecl23); // #4276 - segmentation fault TEST_CASE(vardecl24); // #4187 - variable declaration within lambda function TEST_CASE(vardecl25); // #4799 - segmentation fault TEST_CASE(vardecl26); // #5907 - incorrect handling of extern declarations TEST_CASE(vardecl27); // #7850 - crash on valid C code TEST_CASE(vardecl_stl_1); TEST_CASE(vardecl_stl_2); TEST_CASE(vardecl_template_1); TEST_CASE(vardecl_template_2); TEST_CASE(vardecl_union); TEST_CASE(vardecl_par); // #2743 - set links if variable type contains parentheses TEST_CASE(vardecl_par2); // #3912 - set correct links TEST_CASE(vardecl_par3); // #6556 - Fred x1(a), x2(b); TEST_CASE(vardecl_class_ref); TEST_CASE(volatile_variables); // unsigned i; => unsigned int i; TEST_CASE(unsigned1); TEST_CASE(unsigned2); TEST_CASE(unsigned3); // template arguments TEST_CASE(simplifyStdType); // #4947, #4950, #4951 TEST_CASE(createLinks); TEST_CASE(createLinks2); TEST_CASE(signed1); TEST_CASE(simplifyString); TEST_CASE(simplifyConst); TEST_CASE(switchCase); TEST_CASE(simplifyPointerToStandardType); TEST_CASE(functionpointer1); TEST_CASE(functionpointer2); TEST_CASE(functionpointer3); TEST_CASE(functionpointer4); TEST_CASE(functionpointer5); TEST_CASE(functionpointer6); TEST_CASE(functionpointer7); TEST_CASE(functionpointer8); // #7410 - throw TEST_CASE(functionpointer9); // #6113 - function call with function pointer TEST_CASE(removeRedundantAssignment); TEST_CASE(removedeclspec); TEST_CASE(removeattribute); TEST_CASE(functionAttributeBefore); TEST_CASE(functionAttributeAfter); TEST_CASE(cpp03template1); TEST_CASE(cpp0xtemplate1); TEST_CASE(cpp0xtemplate2); TEST_CASE(cpp0xtemplate3); TEST_CASE(cpp0xtemplate4); // Ticket #6181: Mishandled C++11 syntax TEST_CASE(cpp14template); // Ticket #6708 TEST_CASE(arraySize); TEST_CASE(labels); TEST_CASE(simplifyInitVar); TEST_CASE(simplifyInitVar2); TEST_CASE(simplifyInitVar3); TEST_CASE(bitfields1); TEST_CASE(bitfields2); TEST_CASE(bitfields3); TEST_CASE(bitfields4); // ticket #1956 TEST_CASE(bitfields5); // ticket #1956 TEST_CASE(bitfields6); // ticket #2595 TEST_CASE(bitfields7); // ticket #1987 TEST_CASE(bitfields8); TEST_CASE(bitfields9); // ticket #2706 TEST_CASE(bitfields10); TEST_CASE(bitfields12); // ticket #3485 (segmentation fault) TEST_CASE(bitfields13); // ticket #3502 (segmentation fault) TEST_CASE(bitfields14); // ticket #4561 (segfault for 'class a { signals: };') TEST_CASE(bitfields15); // ticket #7747 (enum Foo {A,B}:4;) TEST_CASE(simplifyNamespaceStd); TEST_CASE(microsoftMemory); TEST_CASE(borland); TEST_CASE(Qt); TEST_CASE(simplifySQL); TEST_CASE(simplifyCAlternativeTokens); TEST_CASE(simplifyCalculations); // x = ({ 123; }); => { x = 123; } TEST_CASE(simplifyRoundCurlyParentheses); TEST_CASE(simplifyOperatorName1); TEST_CASE(simplifyOperatorName2); TEST_CASE(simplifyOperatorName3); TEST_CASE(simplifyOperatorName4); TEST_CASE(simplifyOperatorName5); TEST_CASE(simplifyOperatorName6); // ticket #3194 TEST_CASE(simplifyOperatorName7); // ticket #4619 TEST_CASE(simplifyOperatorName8); // ticket #5706 TEST_CASE(simplifyOperatorName9); // ticket #5709 - comma operator not properly tokenized TEST_CASE(simplifyNullArray); // Some simple cleanups of unhandled macros in the global scope TEST_CASE(removeMacrosInGlobalScope); TEST_CASE(removeMacroInVarDecl); // a = b = 0; TEST_CASE(multipleAssignment); TEST_CASE(sizeOfCharLiteral); TEST_CASE(platformWin); TEST_CASE(platformWin32); TEST_CASE(platformWin32A); TEST_CASE(platformWin32W); TEST_CASE(platformWin64); TEST_CASE(platformUnix32); TEST_CASE(platformUnix64); TEST_CASE(platformWin32AStringCat); // ticket #5015 TEST_CASE(platformWin32WStringCat); // ticket #5015 TEST_CASE(platformWinWithNamespace); TEST_CASE(simplifyMathFunctions_sqrt); TEST_CASE(simplifyMathFunctions_cbrt); TEST_CASE(simplifyMathFunctions_exp); TEST_CASE(simplifyMathFunctions_exp2); TEST_CASE(simplifyMathFunctions_logb); TEST_CASE(simplifyMathFunctions_log1p); TEST_CASE(simplifyMathFunctions_ilogb); TEST_CASE(simplifyMathFunctions_log10); TEST_CASE(simplifyMathFunctions_log); TEST_CASE(simplifyMathFunctions_log2); TEST_CASE(simplifyMathFunctions_pow); TEST_CASE(simplifyMathFunctions_fmin); TEST_CASE(simplifyMathFunctions_fmax); TEST_CASE(simplifyMathFunctions_acosh); TEST_CASE(simplifyMathFunctions_acos); TEST_CASE(simplifyMathFunctions_cosh); TEST_CASE(simplifyMathFunctions_cos); TEST_CASE(simplifyMathFunctions_erfc); TEST_CASE(simplifyMathFunctions_erf); TEST_CASE(simplifyMathFunctions_sin); TEST_CASE(simplifyMathFunctions_sinh); TEST_CASE(simplifyMathFunctions_asin); TEST_CASE(simplifyMathFunctions_asinh); TEST_CASE(simplifyMathFunctions_tan); TEST_CASE(simplifyMathFunctions_tanh); TEST_CASE(simplifyMathFunctions_atan); TEST_CASE(simplifyMathFunctions_atanh); TEST_CASE(simplifyMathFunctions_expm1); TEST_CASE(simplifyMathExpressions); //ticket #1620 TEST_CASE(simplifyStaticConst); TEST_CASE(simplifyDeprecated); TEST_CASE(simplifyCaseRange); TEST_CASE(compileLimits); // #5592 crash: gcc: testsuit: gcc.c-torture/compile/limits-declparen.c TEST_CASE(prepareTernaryOpForAST); // AST data TEST_CASE(astexpr); TEST_CASE(astexpr2); // limit large expressions TEST_CASE(astpar); TEST_CASE(astnewdelete); TEST_CASE(astbrackets); TEST_CASE(astunaryop); TEST_CASE(astfunction); TEST_CASE(asttemplate); TEST_CASE(astcast); TEST_CASE(astlambda); TEST_CASE(astcase); TEST_CASE(startOfExecutableScope); TEST_CASE(removeMacroInClassDef); // #6058 TEST_CASE(sizeofAddParentheses); // Make sure the Tokenizer::findGarbageCode() does not have false positives // The TestGarbage ensures that there are true positives TEST_CASE(findGarbageCode); // --check-config TEST_CASE(checkConfiguration); } std::string tokenizeAndStringify(const char code[], bool simplify = false, bool expand = true, Settings::PlatformType platform = Settings::Native, const char* filename = "test.cpp", bool cpp11 = true) { errout.str(""); settings1.debugwarnings = true; settings1.platform(platform); settings1.standards.cpp = cpp11 ? Standards::CPP11 : Standards::CPP03; // tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); // filter out ValueFlow messages.. const std::string debugwarnings = errout.str(); errout.str(""); std::istringstream istr2(debugwarnings.c_str()); std::string line; while (std::getline(istr2,line)) { if (line.find("valueflow.cpp") == std::string::npos) errout << line << "\n"; } if (tokenizer.tokens()) return tokenizer.tokens()->stringifyList(false, expand, false, true, false, 0, 0); else return ""; } std::string tokenizeAndStringifyWindows(const char code[], bool simplify = false, bool expand = true, Settings::PlatformType platform = Settings::Native, const char* filename = "test.cpp", bool cpp11 = true) { errout.str(""); settings_windows.debugwarnings = true; settings_windows.platform(platform); settings_windows.standards.cpp = cpp11 ? Standards::CPP11 : Standards::CPP03; // tokenize.. Tokenizer tokenizer(&settings_windows, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); // filter out ValueFlow messages.. const std::string debugwarnings = errout.str(); errout.str(""); std::istringstream istr2(debugwarnings.c_str()); std::string line; while (std::getline(istr2,line)) { if (line.find("valueflow.cpp") == std::string::npos) errout << line << "\n"; } if (tokenizer.tokens()) return tokenizer.tokens()->stringifyList(false, expand, false, true, false, 0, 0); else return ""; } std::string tokenizeDebugListing(const char code[], bool simplify = false, const char filename[] = "test.cpp") { errout.str(""); settings2.standards.c = Standards::C89; settings2.standards.cpp = Standards::CPP03; Tokenizer tokenizer(&settings2, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); // result.. return tokenizer.tokens()->stringifyList(true,true,true,true,false); } void tokenize1() { const char code[] = "void f ( )\n" "{ if ( p . y ( ) > yof ) { } }"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } void tokenize2() { const char code[] = "{ sizeof a, sizeof b }"; ASSERT_EQUALS("{ sizeof ( a ) , sizeof ( b ) }", tokenizeAndStringify(code)); } void tokenize3() { const char code[] = "void foo()\n" "{\n" " int i;\n" " ABC(for(i=0;i<10;i++) x());\n" "}"; ASSERT_EQUALS("void foo ( )\n" "{\n" "int i ;\n" "ABC ( for ( i = 0 ; i < 10 ; i ++ ) x ( ) ) ;\n" "}", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } void tokenize4() { const char code[] = "class foo\n" "{\n" "public:\n" " const int i;\n" "}"; ASSERT_EQUALS("class foo\n" "{\n" "public:\n" "const int i ;\n" "}", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } void tokenize5() { // Tokenize values ASSERT_EQUALS("; + 1E3 ;", tokenizeAndStringify("; +1E3 ;")); ASSERT_EQUALS("; 1E-2 ;", tokenizeAndStringify("; 1E-2 ;")); } void tokenize6() { // "&p[1]" => "p+1" /* ASSERT_EQUALS("; x = p + n ;", tokenizeAndStringify("; x = & p [ n ] ;", true)); ASSERT_EQUALS("; x = ( p + n ) [ m ] ;", tokenizeAndStringify("; x = & p [ n ] [ m ] ;", true)); ASSERT_EQUALS("; x = y & p [ n ] ;", tokenizeAndStringify("; x = y & p [ n ] ;", true)); ASSERT_EQUALS("; x = 10 & p [ n ] ;", tokenizeAndStringify("; x = 10 & p [ n ] ;", true)); ASSERT_EQUALS("; x = y [ 10 ] & p [ n ] ;", tokenizeAndStringify("; x = y [ 10 ] & p [ n ] ;", true)); ASSERT_EQUALS("; x = ( a + m ) & p [ n ] ;", tokenizeAndStringify("; x = ( a + m ) & p [ n ] ;", true));*/ // "*(p+1)" => "p[1]" ASSERT_EQUALS("; x = p [ 1 ] ;", tokenizeAndStringify("; x = * ( p + 1 ) ;", true)); ASSERT_EQUALS("; x = p [ 10 ] ;", tokenizeAndStringify("; x = * ( p + 0xA ) ;", true)); ASSERT_EQUALS("; x = p [ n ] ;", tokenizeAndStringify("; x = * ( p + n ) ;", true)); ASSERT_EQUALS("; x = y * ( p + n ) ;", tokenizeAndStringify("; x = y * ( p + n ) ;", true)); ASSERT_EQUALS("; x = 10 * ( p + n ) ;", tokenizeAndStringify("; x = 10 * ( p + n ) ;", true)); ASSERT_EQUALS("; x = y [ 10 ] * ( p + n ) ;", tokenizeAndStringify("; x = y [ 10 ] * ( p + n ) ;", true)); ASSERT_EQUALS("; x = ( a + m ) * ( p + n ) ;", tokenizeAndStringify("; x = ( a + m ) * ( p + n ) ;", true)); // "*(p-1)" => "p[-1]" and "*(p-n)" => "p[-n]" ASSERT_EQUALS("; x = p [ -1 ] ;", tokenizeAndStringify("; x = *(p - 1);", true)); ASSERT_EQUALS("; x = p [ -10 ] ;", tokenizeAndStringify("; x = *(p - 0xA);", true)); ASSERT_EQUALS("; x = p [ - n ] ;", tokenizeAndStringify("; x = *(p - n);", true)); ASSERT_EQUALS("; x = y * ( p - 1 ) ;", tokenizeAndStringify("; x = y * (p - 1);", true)); ASSERT_EQUALS("; x = 10 * ( p - 1 ) ;", tokenizeAndStringify("; x = 10 * (p - 1);", true)); ASSERT_EQUALS("; x = y [ 10 ] * ( p - 1 ) ;", tokenizeAndStringify("; x = y[10] * (p - 1);", true)); ASSERT_EQUALS("; x = ( a - m ) * ( p - n ) ;", tokenizeAndStringify("; x = (a - m) * (p - n);", true)); // Test that the array-index simplification is not applied when there's no dereference: // "(x-y)" => "(x-y)" and "(x+y)" => "(x+y)" ASSERT_EQUALS("; a = b * ( x - y ) ;", tokenizeAndStringify("; a = b * (x - y);", true)); ASSERT_EQUALS("; a = b * x [ - y ] ;", tokenizeAndStringify("; a = b * *(x - y);", true)); ASSERT_EQUALS("; a = a * ( x - y ) ;", tokenizeAndStringify("; a *= (x - y);", true)); ASSERT_EQUALS("; z = a ++ * ( x - y ) ;", tokenizeAndStringify("; z = a++ * (x - y);", true)); ASSERT_EQUALS("; z = a ++ * ( x + y ) ;", tokenizeAndStringify("; z = a++ * (x + y);", true)); ASSERT_EQUALS("; z = a -- * ( x - y ) ;", tokenizeAndStringify("; z = a-- * (x - y);", true)); ASSERT_EQUALS("; z = a -- * ( x + y ) ;", tokenizeAndStringify("; z = a-- * (x + y);", true)); ASSERT_EQUALS("; z = 'a' * ( x - y ) ;", tokenizeAndStringify("; z = 'a' * (x - y);", true)); ASSERT_EQUALS("; z = \"a\" * ( x - y ) ;", tokenizeAndStringify("; z = \"a\" * (x - y);", true)); ASSERT_EQUALS("; z = 'a' * ( x + y ) ;", tokenizeAndStringify("; z = 'a' * (x + y);", true)); ASSERT_EQUALS("; z = \"a\" * ( x + y ) ;", tokenizeAndStringify("; z = \"a\" * (x + y);", true)); ASSERT_EQUALS("; z = foo ( ) * ( x + y ) ;", tokenizeAndStringify("; z = foo() * (x + y);", true)); } void tokenize7() { const char code[] = "void f() {\n" " int x1 = 1;\n" " int x2(x1);\n" "}\n"; ASSERT_EQUALS("void f ( ) {\nint x1 ; x1 = 1 ;\nint x2 ; x2 = x1 ;\n}", tokenizeAndStringify(code, false)); } void tokenize8() { const char code[] = "void f() {\n" " int x1(g());\n" " int x2(x1);\n" "}\n"; ASSERT_EQUALS("1: void f ( ) {\n" "2: int x1@1 ; x1@1 = g ( ) ;\n" "3: int x2@2 ; x2@2 = x1@1 ;\n" "4: }\n", tokenizeDebugListing(code, false)); } void tokenize9() { const char code[] = "typedef void (*fp)();\n" "typedef fp (*fpp)();\n" "void f() {\n" " fpp x = (fpp)f();\n" "}"; tokenizeAndStringify(code, false); ASSERT_EQUALS("", errout.str()); } void tokenize11() { ASSERT_EQUALS("X * sizeof ( Y ( ) ) ;", tokenizeAndStringify("X * sizeof(Y());", false)); } // bailout if there is "@" - it is not handled well void tokenize13() { const char code[] = "@implementation\n" "-(Foo *)foo: (Bar *)bar\n" "{ }\n" "@end\n"; ASSERT_THROW(tokenizeAndStringify(code), InternalError); } // Ticket #2361: 0X10 => 16 void tokenize14() { ASSERT_EQUALS("; 16 ;", tokenizeAndStringify(";0x10;")); ASSERT_EQUALS("; 16 ;", tokenizeAndStringify(";0X10;")); ASSERT_EQUALS("; 292 ;", tokenizeAndStringify(";0444;")); } // Ticket #8050 void tokenizeHexWithSuffix() { ASSERT_EQUALS("; 16777215 ;", tokenizeAndStringify(";0xFFFFFF;")); ASSERT_EQUALS("; 16777215U ;", tokenizeAndStringify(";0xFFFFFFu;")); ASSERT_EQUALS("; 16777215UL ;", tokenizeAndStringify(";0xFFFFFFul;")); // Number of digits decides about internal representation... ASSERT_EQUALS("; 4294967295U ;", tokenizeAndStringify(";0xFFFFFFFF;")); ASSERT_EQUALS("; 4294967295U ;", tokenizeAndStringify(";0xFFFFFFFFu;")); ASSERT_EQUALS("; 4294967295UL ;", tokenizeAndStringify(";0xFFFFFFFFul;")); } // Ticket #2429: 0.125 void tokenize15() { ASSERT_EQUALS("0.125 ;", tokenizeAndStringify(".125;")); ASSERT_EQUALS("005.125 ;", tokenizeAndStringify("005.125;")); // Don't confuse with octal values } void tokenize17() { // #2759 ASSERT_EQUALS("class B : private :: A { } ;", tokenizeAndStringify("class B : private ::A { };")); } void tokenize18() { // tokenize "(X&&Y)" into "( X && Y )" instead of "( X & & Y )" ASSERT_EQUALS("( X && Y ) ;", tokenizeAndStringify("(X&&Y);")); } void tokenize19() { // #3006 - added hasComplicatedSyntaxErrorsInTemplates to avoid segmentation fault ASSERT_THROW(tokenizeAndStringify("x < () <"), InternalError); // #3496 - make sure hasComplicatedSyntaxErrorsInTemplates works ASSERT_EQUALS("void a ( Fred * f ) { for ( ; n < f . x ( ) ; ) { } }", tokenizeAndStringify("void a(Fred* f) MACRO { for (;n < f->x();) {} }")); // #6216 - make sure hasComplicatedSyntaxErrorsInTemplates works ASSERT_EQUALS("C :: C ( )\n" ": v { }\n" "{\n" "for ( int dim = 0 ; dim < v . size ( ) ; ++ dim ) {\n" "v [ dim ] . f ( ) ;\n" "}\n" "} ;", tokenizeAndStringify("C::C()\n" ":v{}\n" "{\n" " for (int dim = 0; dim < v.size(); ++dim) {\n" " v[dim]->f();\n" " }\n" "};")); } void tokenize21() { // tokenize 0x0E-7 ASSERT_EQUALS("14 - 7 ;", tokenizeAndStringify("0x0E-7;")); } void tokenize22() { // tokenize special marker $ from preprocessor ASSERT_EQUALS("a$b", tokenizeAndStringify("a$b")); ASSERT_EQUALS("a $b\nc", tokenizeAndStringify("a $b\nc")); ASSERT_EQUALS("a = $0 ;", tokenizeAndStringify("a = $0;")); ASSERT_EQUALS("a$ ++ ;", tokenizeAndStringify("a$++;")); ASSERT_EQUALS("$if ( ! p )", tokenizeAndStringify("$if(!p)")); } // #4239 - segfault for "f ( struct { int typedef T x ; } ) { }" void tokenize25() { ASSERT_THROW(tokenizeAndStringify("f ( struct { int typedef T x ; } ) { }"), InternalError); } // #4245 - segfault void tokenize26() { ASSERT_THROW(tokenizeAndStringify("class x { protected : template < int y = } ;"), InternalError); // Garbage code } void tokenize27() { // #4525 - segfault tokenizeAndStringify("struct except_spec_d_good : except_spec_a, except_spec_b {\n" "~except_spec_d_good();\n" "};\n" "struct S { S(); };\n" "S::S() __attribute((pure)) = default;" ); // original code: glibc-2.18/posix/bug-regex20.c tokenizeAndStringify("static unsigned int re_string_context_at (const re_string_t *input, int idx, int eflags) internal_function __attribute__ ((pure));"); } // #3503 - don't "simplify" SetFunction member function to a variable void tokenize31() { ASSERT_EQUALS("struct TTestClass { TTestClass ( ) { }\n" "void SetFunction ( Other * m_f ) { }\n" "} ;", tokenizeAndStringify("struct TTestClass { TTestClass() { }\n" " void SetFunction(Other(*m_f)()) { }\n" "};")); ASSERT_EQUALS("struct TTestClass { TTestClass ( ) { }\n" "void SetFunction ( Other * m_f ) ;\n" "} ;", tokenizeAndStringify("struct TTestClass { TTestClass() { }\n" " void SetFunction(Other(*m_f)());\n" "};")); } // #5884 - Avoid left shift of negative integer value. void tokenize32() { // Do not simplify negative integer left shifts. const char * code = "void f ( ) { int max_x ; max_x = -10000 << 16 ; }"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } // #5780 Various crashes on valid template code in Tokenizer::setVarId() void tokenize33() { const char * code = "template> struct vector {};\n" "void z() {\n" " vector VI;\n" "}\n"; tokenizeAndStringify(code, true); } void tokenize34() { // #8031 { const char code[] = "struct Containter {\n" " Containter();\n" " int* mElements;\n" "};\n" "Containter::Containter() : mElements(nullptr) {}\n" "Containter intContainer;"; const char exp [] = "1: struct Containter {\n" "2: Containter ( ) ;\n" "3: int * mElements@1 ;\n" "4: } ;\n" "5: Containter :: Containter ( ) : mElements@1 ( nullptr ) { }\n" "6: Containter intContainer@2 ;\n"; ASSERT_EQUALS(exp, tokenizeDebugListing(code, /*simplify=*/true)); } { const char code[] = "template struct Containter {\n" " Containter();\n" " int* mElements;\n" "};\n" "template Containter::Containter() : mElements(nullptr) {}\n" "Containter intContainer;"; const char exp [] = "5: template < class T > Containter < T > :: Containter ( ) : mElements ( nullptr ) { }\n" "6: Containter intContainer@1 ; struct Containter {\n" "2: Containter ( ) ;\n" "3: int * mElements@2 ;\n" "4: } ;\n" "5: Containter :: Containter ( ) : mElements@2 ( nullptr ) { }\n"; ASSERT_EQUALS(exp, tokenizeDebugListing(code, /*simplify=*/true)); } } void validate() { // C++ code in C file ASSERT_THROW(tokenizeAndStringify(";using namespace std;",false,false,Settings::Native,"test.c"), InternalError); ASSERT_THROW(tokenizeAndStringify(";std::map m;",false,false,Settings::Native,"test.c"), InternalError); ASSERT_THROW(tokenizeAndStringify(";template class X { };",false,false,Settings::Native,"test.c"), InternalError); ASSERT_THROW(tokenizeAndStringify("int X() {};",false,false,Settings::Native,"test.c"), InternalError); ASSERT_THROW(tokenizeAndStringify("void foo(int i) { reinterpret_cast(i) };",false,false,Settings::Native,"test.h"), InternalError); } void syntax_case_default() { // correct syntax tokenizeAndStringify("void f() {switch (n) { case 0: z(); break;}}"); ASSERT_EQUALS("", errout.str()); tokenizeAndStringify("void f() {switch (n) { case 0:; break;}}"); ASSERT_EQUALS("", errout.str()); tokenizeAndStringify("void f() {switch (n) { case 0?1:2 : z(); break;}}"); ASSERT_EQUALS("", errout.str()); tokenizeAndStringify("void f() {switch (n) { case 0?(1?3:4):2 : z(); break;}}"); ASSERT_EQUALS("", errout.str()); //allow GCC '({ %name%|%num%|%bool% ; })' statement expression extension tokenizeAndStringify("void f() {switch (n) { case 0?({0;}):1: z(); break;}}"); ASSERT_EQUALS("", errout.str()); //'b' can be or a macro or an undefined enum tokenizeAndStringify("void f() {switch (n) { case b: z(); break;}}"); ASSERT_EQUALS("", errout.str()); //valid, when there's this declaration: 'constexpr int g() { return 2; }' tokenizeAndStringify("void f() {switch (n) { case g(): z(); break;}}"); ASSERT_EQUALS("", errout.str()); //valid, when there's also this declaration: 'constexpr int g[1] = {0};' tokenizeAndStringify("void f() {switch (n) { case g[0]: z(); break;}}"); ASSERT_EQUALS("", errout.str()); //valid, similar to above case tokenizeAndStringify("void f() {switch (n) { case *g: z(); break;}}"); ASSERT_EQUALS("", errout.str()); //valid, when 'x' and 'y' are constexpr. tokenizeAndStringify("void f() {switch (n) { case sqrt(x+y): z(); break;}}"); ASSERT_EQUALS("", errout.str()); } void foreach () { // #3690,#5154 const char code[] ="void f() { for each ( char c in MyString ) { Console::Write(c); } }"; ASSERT_EQUALS("void f ( ) { asm ( \"char c in MyString\" ) { Console :: Write ( c ) ; } }", tokenizeAndStringify(code)); } void combineOperators() { ASSERT_EQUALS("; private: ;", tokenizeAndStringify(";private:;", false)); ASSERT_EQUALS("; protected: ;", tokenizeAndStringify(";protected:;", false)); ASSERT_EQUALS("; public: ;", tokenizeAndStringify(";public:;", false)); ASSERT_EQUALS("; __published: ;", tokenizeAndStringify(";__published:;", false)); ASSERT_EQUALS("a . public : ;", tokenizeAndStringify("a.public:;", false)); } void concatenateNegativeNumber() { ASSERT_EQUALS("i = -12 ;", tokenizeAndStringify("i = -12;")); ASSERT_EQUALS("1 - 2 ;", tokenizeAndStringify("1-2;")); ASSERT_EQUALS("foo ( -1 ) - 2 ;", tokenizeAndStringify("foo(-1)-2;")); ASSERT_EQUALS("int f ( ) { return -2 ; }", tokenizeAndStringify("int f(){return -2;}")); ASSERT_EQUALS("int x [ 2 ] = { -2 , 1 }", tokenizeAndStringify("int x[2] = {-2,1}")); ASSERT_EQUALS("f ( 123 )", tokenizeAndStringify("f(+123)")); } void longtok() { const std::string filedata(10000, 'a'); ASSERT_EQUALS(filedata, tokenizeAndStringify(filedata.c_str(), true)); } // Don’t remove "(int *)".. void simplifyCasts1() { const char code[] = "int *f(int *);"; ASSERT_EQUALS("int * f ( int * ) ;", tokenizeAndStringify(code, true)); } // remove static_cast.. void simplifyCasts2() { const char code[] = "t = (static_cast *>(&p));\n"; ASSERT_EQUALS("t = & p ;", tokenizeAndStringify(code, true)); } void simplifyCasts3() { // ticket #961 const char code[] = "assert (iplen >= (unsigned) ipv4->ip_hl * 4 + 20);"; const char expected[] = "assert ( iplen >= ipv4 . ip_hl * 4 + 20 ) ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyCasts4() { // ticket #970 const char code[] = "if (a >= (unsigned)(b)) {}"; const char expected[] = "if ( a >= ( unsigned int ) ( b ) ) { }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyCasts5() { // ticket #1817 ASSERT_EQUALS("a . data = f ;", tokenizeAndStringify("a->data = reinterpret_cast(static_cast(f));", true)); } void simplifyCasts7() { ASSERT_EQUALS("str = malloc ( 3 )", tokenizeAndStringify("str=(char **)malloc(3)", true)); } void simplifyCasts8() { ASSERT_EQUALS("ptr1 = ptr2", tokenizeAndStringify("ptr1=(int * **)ptr2", true)); } void simplifyCasts9() { ASSERT_EQUALS("f ( ( double ) ( v1 ) * v2 )", tokenizeAndStringify("f((double)(v1)*v2)", true)); ASSERT_EQUALS("int v1 ; f ( ( double ) ( v1 ) * v2 )", tokenizeAndStringify("int v1; f((double)(v1)*v2)", true)); ASSERT_EQUALS("f ( ( A ) ( B ) & x )", tokenizeAndStringify("f((A)(B)&x)", true)); // #4439 } void simplifyCasts10() { ASSERT_EQUALS("; ( * f ) ( p ) ;", tokenizeAndStringify("; (*(void (*)(char *))f)(p);", true)); } void simplifyCasts11() { ASSERT_EQUALS("; x = 0 ;", tokenizeAndStringify("; *(int *)&x = 0;", true)); } void simplifyCasts12() { // #3935 - don't remove this cast ASSERT_EQUALS("; ( ( short * ) data ) [ 5 ] = 0 ;", tokenizeAndStringify("; ((short*)data)[5] = 0;", true)); } void simplifyCasts13() { // casting deref / address of ASSERT_EQUALS("; int x ; x = * y ;", tokenizeAndStringify(";int x=(int)*y;",true)); ASSERT_EQUALS("; int x ; x = & y ;", tokenizeAndStringify(";int x=(int)&y;",true)); TODO_ASSERT_EQUALS("; int x ; x = ( INT ) * y ;", "; int x ; x = * y ;", tokenizeAndStringify(";int x=(INT)*y;",true)); // INT might be a variable TODO_ASSERT_EQUALS("; int x ; x = ( INT ) & y ;", "; int x ; x = & y ;", tokenizeAndStringify(";int x=(INT)&y;",true)); // INT might be a variable // #4899 - False positive on unused variable ASSERT_EQUALS("; float angle ; angle = tilt ;", tokenizeAndStringify("; float angle = (float) tilt;", true)); // status quo ASSERT_EQUALS("; float angle ; angle = ( float ) - tilt ;", tokenizeAndStringify("; float angle = (float) -tilt;", true)); ASSERT_EQUALS("; float angle ; angle = ( float ) + tilt ;", tokenizeAndStringify("; float angle = (float) +tilt;", true)); ASSERT_EQUALS("; int a ; a = ( int ) ~ c ;", tokenizeAndStringify("; int a = (int)~c;", true)); } void simplifyCasts14() { // const // #5081 ASSERT_EQUALS("( ! ( & s ) . a ) ;", tokenizeAndStringify("(! ( (struct S const *) &s)->a);", true)); // #5244 ASSERT_EQUALS("bar ( & ptr ) ;", tokenizeAndStringify("bar((const X**)&ptr);",true)); } void simplifyCasts15() { // #5996 - don't remove cast in 'a+static_cast(b?60:0)' ASSERT_EQUALS("a + ( b ? 60 : 0 ) ;", tokenizeAndStringify("a + static_cast(b ? 60 : 0);", true)); } void simplifyCasts16() { // #6278 ASSERT_EQUALS("Get ( pArray ) ;", tokenizeAndStringify("Get((CObject*&)pArray);", true)); } void simplifyCasts17() { // #6110 - don't remove any parentheses in 'a(b)(c)' ASSERT_EQUALS("if ( a ( b ) ( c ) >= 3 )", tokenizeAndStringify("if (a(b)(c) >= 3)", true)); } void inlineasm() { ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ;", tokenizeAndStringify("asm { mov ax,bx };")); ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ;", tokenizeAndStringify("_asm { mov ax,bx };")); ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ;", tokenizeAndStringify("_asm mov ax,bx")); ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ;", tokenizeAndStringify("__asm { mov ax,bx };")); ASSERT_EQUALS("asm ( \"\"mov ax,bx\"\" ) ;", tokenizeAndStringify("__asm__ __volatile__ ( \"mov ax,bx\" );")); ASSERT_EQUALS("asm ( \"_emit 12h\" ) ;", tokenizeAndStringify("__asm _emit 12h ;")); ASSERT_EQUALS("asm ( \"mov a , b\" ) ;", tokenizeAndStringify("__asm mov a, b ;")); ASSERT_EQUALS("asm ( \"\"fnstcw %0\" : \"= m\" ( old_cw )\" ) ;", tokenizeAndStringify("asm volatile (\"fnstcw %0\" : \"= m\" (old_cw));")); ASSERT_EQUALS("asm ( \"\"fnstcw %0\" : \"= m\" ( old_cw )\" ) ;", tokenizeAndStringify(" __asm__ (\"fnstcw %0\" : \"= m\" (old_cw));")); ASSERT_EQUALS("asm ( \"\"ddd\"\" ) ;", tokenizeAndStringify(" __asm __volatile__ (\"ddd\") ;")); ASSERT_EQUALS("asm ( \"\"ddd\"\" ) ;", tokenizeAndStringify(" __asm __volatile (\"ddd\") ;")); ASSERT_EQUALS("asm ( \"\"mov ax,bx\"\" ) ;", tokenizeAndStringify("__asm__ volatile ( \"mov ax,bx\" );")); ASSERT_EQUALS("asm ( \"mov ax , bx\" ) ; int a ;", tokenizeAndStringify("asm { mov ax,bx } int a;")); ASSERT_EQUALS("asm\n\n( \"mov ax , bx\" ) ;", tokenizeAndStringify("__asm\nmov ax,bx\n__endasm;")); ASSERT_EQUALS("asm\n\n( \"push b ; for if\" ) ;", tokenizeAndStringify("__asm\npush b ; for if\n__endasm;")); // 'asm ( ) ;' should be in the same line ASSERT_EQUALS(";\n\nasm ( \"\"mov ax,bx\"\" ) ;", tokenizeAndStringify(";\n\n__asm__ volatile ( \"mov ax,bx\" );", true)); } // #4725 - ^{} void simplifyAsm2() { ASSERT_EQUALS("void f ( ) { asm ( \"^{}\" ) ; }", tokenizeAndStringify("void f() { ^{} }")); ASSERT_EQUALS("void f ( ) { x ( asm ( \"^{}\" ) ) ; }", tokenizeAndStringify("void f() { x(^{}); }")); ASSERT_EQUALS("void f ( ) { foo ( A ( ) , asm ( \"^{bar();}\" ) ) ; }", tokenizeAndStringify("void f() { foo(A(), ^{ bar(); }); }")); ASSERT_EQUALS("int f0 ( Args args ) { asm ( \"asm(\"return^{returnsizeof...(Args);}()\")+^{returnsizeof...(args);}()\" )\n" "2:\n" "|\n" "5:\n" "6: ;\n" "} ;", tokenizeAndStringify("int f0(Args args) {\n" " return ^{\n" " return sizeof...(Args);\n" " }() + ^ {\n" " return sizeof...(args);\n" " }();\n" "};")); ASSERT_EQUALS("int ( ^ block ) ( void ) = asm ( \"^{staticinttest=0;returntest;}\" )\n\n\n;", tokenizeAndStringify("int(^block)(void) = ^{\n" " static int test = 0;\n" " return test;\n" "};")); ASSERT_EQUALS("; return f ( a [ b = c ] , asm ( \"^{}\" ) ) ;", tokenizeAndStringify("; return f(a[b=c],^{});")); // #7185 ASSERT_EQUALS("; return f ( asm ( \"^(void){somecode}\" ) ) ;", tokenizeAndStringify("; return f(^(void){somecode});")); ASSERT_EQUALS("; asm ( \"a?(b?(c,asm(\"^{}\")):0):^{}\" ) ;", tokenizeAndStringify(";a?(b?(c,^{}):0):^{};")); } void ifAddBraces1() { const char code[] = "void f()\n" "{\n" " if (a);\n" " else ;\n" "}\n"; ASSERT_EQUALS("void f ( )\n" "{\n" "if ( a ) { ; }\n" "else { ; }\n" "}", tokenizeAndStringify(code, true)); } void ifAddBraces2() { const char code[] = "void f()\n" "{\n" " if (a) if (b) { }\n" "}\n"; ASSERT_EQUALS("void f ( )\n" "{\n" "if ( a ) { if ( b ) { } }\n" "}", tokenizeAndStringify(code, true)); } void ifAddBraces3() { const char code[] = "void f()\n" "{\n" " if (a) for (;;) { }\n" "}\n"; ASSERT_EQUALS("void f ( )\n" "{\n" "if ( a ) { for ( ; ; ) { } }\n" "}", tokenizeAndStringify(code, true)); } void ifAddBraces4() { const char code[] = "char * foo ()\n" "{\n" " char *str = malloc(10);\n" " if (somecondition)\n" " for ( ; ; )\n" " { }\n" " return str;\n" "}\n"; ASSERT_EQUALS("char * foo ( )\n" "{\n" "char * str ; str = malloc ( 10 ) ;\n" "if ( somecondition ) {\n" "for ( ; ; )\n" "{ } }\n" "return str ;\n" "}", tokenizeAndStringify(code, true)); } void ifAddBraces5() { const char code[] = "void f()\n" "{\n" "for(int i = 0; i < 2; i++)\n" "if(true)\n" "return;\n" "\n" "return;\n" "}\n"; ASSERT_EQUALS("void f ( )\n" "{\n" "for ( int i = 0 ; i < 2 ; i ++ ) {\n" "\n" "return ; }\n\n" "return ;\n" "}", tokenizeAndStringify(code, true)); } void ifAddBraces7() { const char code[] = "void f()\n" "{\n" "int a;\n" "if( a )\n" " ({a=4;}),({a=5;});\n" "}\n"; ASSERT_EQUALS("void f ( )\n" "{\n" "int a ;\n" "if ( a ) {\n" "( { a = 4 ; } ) , ( { a = 5 ; } ) ; }\n" "}", tokenizeAndStringify(code, true)); } void ifAddBraces9() { // ticket #990 const char code[] = "void f() {" " for (int k=0; k 0 ) ;\n" " return 0 ;\n" "}\n"; const char result[] = "void foo ( int c , int d ) {\n" "do {\n" "if ( c ) {\n" "while ( c ) { c -- ; }\n" "} }\n" "while ( -- d > 0 ) ;\n" "return 0 ;\n" "}"; ASSERT_EQUALS(result, tokenizeAndStringify(code, true)); } { const char code[] = "void foo ( int c , int d ) {\n" " do\n" " do c -- ; while ( c ) ;\n" " while ( -- d > 0 ) ;\n" " return 0 ;\n" "}\n"; const char result[] = "void foo ( int c , int d ) {\n" "do {\n" "do { c -- ; } while ( c ) ; }\n" "while ( -- d > 0 ) ;\n" "return 0 ;\n" "}"; ASSERT_EQUALS(result, tokenizeAndStringify(code, true)); } { // #8148 - while inside the do-while body const char code[] = "void foo() {\n" " do { while (x) f(); } while (y);\n" "}"; const char result[] = "void foo ( ) {\n" "do { while ( x ) { f ( ) ; } } while ( y ) ;\n" "}"; ASSERT_EQUALS(result, tokenizeAndStringify(code, true)); } } void forAddBraces1() { { const char code[] = "void f() {\n" " for(;;)\n" " if (a) { }\n" " else { }\n" "}"; const char expected[] = "void f ( ) {\n" "for ( ; ; ) {\n" "if ( a ) { }\n" "else { } }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "void f() {\n" " for(;;)\n" " if (a) { }\n" " else if (b) { }\n" " else { }\n" "}"; const char expected[] = "void f ( ) {\n" "for ( ; ; ) {\n" "if ( a ) { }\n" "else { if ( b ) { }\n" "else { } } }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } } void forAddBraces2() { // #5088 const char code[] = "void f() {\n" " for(;;) try { } catch (...) { }\n" "}"; const char expected[] = "void f ( ) {\n" "for ( ; ; ) { try { } catch ( . . . ) { } }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } std::string simplifyKnownVariables(const char code[]) { errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyKnownVariables(); return tokenizer.tokens()->stringifyList(0, false); } void simplifyKnownVariables1() { { const char code[] = "void f()\n" "{\n" " int a = 10;\n" " if (a);\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int a ; a = 10 ; if ( 10 ) { ; } }", simplifyKnownVariables(code)); } { const char code[] = "void f()\n" "{\n" " int a = 10;\n" " if (!a);\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int a ; a = 10 ; if ( ! 10 ) { ; } }", simplifyKnownVariables(code)); } } void simplifyKnownVariables2() { const char code[] = "void f()\n" "{\n" " int a = 10;\n" " a = g();\n" " if (a);\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int a ; a = 10 ; a = g ( ) ; if ( a ) { ; } }", simplifyKnownVariables(code)); } void simplifyKnownVariables3() { const char code[] = "void f()\n" "{\n" " int a = 4;\n" " while(true){\n" " break;\n" " a = 10;\n" " }\n" " if (a);\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int a ; a = 4 ; while ( true ) { break ; a = 10 ; } if ( a ) { ; } }", simplifyKnownVariables(code)); } void simplifyKnownVariables4() { const char code[] = "void f()\n" "{\n" " int a = 4;\n" " if ( g(a));\n" "}\n"; // TODO: if a is passed by value is is ok to simplify.. ASSERT_EQUALS( "void f ( ) { int a ; a = 4 ; if ( g ( a ) ) { ; } }", simplifyKnownVariables(code)); } void simplifyKnownVariables5() { const char code[] = "void f()\n" "{\n" " int a = 4;\n" " if ( a = 5 );\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int a ; a = 4 ; if ( a = 5 ) { ; } }", simplifyKnownVariables(code)); } void simplifyKnownVariables6() { const char code[] = "void f()\n" "{\n" " char str[2];" " int a = 4;\n" " str[a] = 0;\n" "}\n"; ASSERT_EQUALS( "void f ( ) { char str [ 2 ] ; int a ; a = 4 ; str [ 4 ] = 0 ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables7() { const char code[] = "void foo()\n" "{\n" " int i = 22;\n" " abc[i++] = 1;\n" " abc[++i] = 2;\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int i ; i = 24 ; abc [ 22 ] = 1 ; abc [ 24 ] = 2 ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables8() { const char code[] = "void foo()\n" "{\n" " int i = 22;\n" " i++;\n" " abc[i] = 0;\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int i ; i = 23 ; abc [ 23 ] = 0 ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables9() { const char code[] = "void foo()\n" "{\n" " int a = 1, b = 2;\n" " if (a < b)\n" " ;\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int a ; a = 1 ; int b ; b = 2 ; if ( 1 < 2 ) { ; } }", simplifyKnownVariables(code)); } void simplifyKnownVariables10() { { const char code[] = "void f()\n" "{\n" " bool b=false;\n" "\n" " {\n" " b = true;\n" " }\n" "\n" " if( b )\n" " {\n" " a();\n" " }\n" "}\n"; const std::string expected1("void f ( ) {" " bool b ; b = false ;" " { b = true ; }"); TODO_ASSERT_EQUALS( expected1 + " if ( true ) { a ( ) ; } }", expected1 + " if ( b ) { a ( ) ; } }", simplifyKnownVariables(code)); } { const char code[] = "void f()\n" "{\n" " bool b=false;\n" " { b = false; }\n" " {\n" " b = true;\n" " }\n" "\n" " if( b )\n" " {\n" " a();\n" " }\n" "}\n"; TODO_ASSERT_EQUALS( "void f ( ) { bool b ; b = false ; { b = false ; } { b = true ; } if ( true ) { a ( ) ; } }", "void f ( ) { bool b ; b = false ; { b = false ; } { b = true ; } if ( b ) { a ( ) ; } }", simplifyKnownVariables(code)); } { const char code[] = "void f()\n" "{\n" " int b=0;\n" " b = 1;\n" " for( int i = 0; i < 10; i++ )" " {\n" " }\n" "\n" " return b;\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int b ; b = 0 ; b = 1 ; for ( int i = 0 ; i < 10 ; i ++ ) { } return 1 ; }", simplifyKnownVariables(code)); } } void simplifyKnownVariables11() { const char code[] = "const int foo = 0;\n" "int main()\n" "{\n" " int foo=0;\n" "}\n"; ASSERT_EQUALS( "int main ( ) { int foo ; foo = 0 ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables12() { const char code[] = "ENTER_NAMESPACE(project_namespace)\n" "const double pi = 3.14;\n" "int main(){}\n"; ASSERT_EQUALS( "ENTER_NAMESPACE ( project_namespace ) const double pi = 3.14 ; int main ( ) { }", simplifyKnownVariables(code)); } void simplifyKnownVariables13() { const char code[] = "void f()\n" "{\n" " int i = 10;\n" " while(--i) {}\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int i ; i = 10 ; while ( -- i ) { } }", simplifyKnownVariables(code)); } void simplifyKnownVariables14() { // ticket #753 const char code[] = "void f ( ) { int n ; n = 1 ; do { ++ n ; } while ( n < 10 ) ; }"; ASSERT_EQUALS(code, simplifyKnownVariables(code)); } void simplifyKnownVariables15() { { const char code[] = "int main()\n" "{\n" " int x=5;\n" " std::cout << 10 / x << std::endl;\n" "}\n"; ASSERT_EQUALS( "int main ( ) { int x ; x = 5 ; std :: cout << 10 / 5 << std :: endl ; }", simplifyKnownVariables(code)); } { const char code[] = "int main()\n" "{\n" " int x=5;\n" " std::cout << x / ( x == 1 ) << std::endl;\n" "}\n"; ASSERT_EQUALS( "int main ( ) { int x ; x = 5 ; std :: cout << 5 / ( 5 == 1 ) << std :: endl ; }", simplifyKnownVariables(code)); } } void simplifyKnownVariables16() { // ticket #807 - segmentation fault when macro isn't found const char code[] = "void f ( ) { int n = 1; DISPATCH(while); }"; ASSERT_THROW(simplifyKnownVariables(code), InternalError); } void simplifyKnownVariables17() { // ticket #807 - segmentation fault when macro isn't found const char code[] = "void f ( ) { char *s = malloc(100);mp_ptr p = s; p++; }"; ASSERT_EQUALS( "void f ( ) { char * s ; s = malloc ( 100 ) ; mp_ptr p ; p = s ; p ++ ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables18() { const char code[] = "void f ( ) { char *s = malloc(100);mp_ptr p = s; ++p; }"; ASSERT_EQUALS( "void f ( ) { char * s ; s = malloc ( 100 ) ; mp_ptr p ; p = s ; ++ p ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables19() { const char code[] = "void f ( ) { int i=0; do { if (i>0) { a(); } i=b(); } while (i != 12); }"; ASSERT_EQUALS( "void f ( ) { int i ; i = 0 ; do { if ( i > 0 ) { a ( ) ; } i = b ( ) ; } while ( i != 12 ) ; }", simplifyKnownVariables(code)); } void simplifyKnownVariables20() { const char code[] = "void f()\n" "{\n" " int i = 0;\n" " if (x) {\n" " if (i) i=0;\n" " }\n" "}\n"; ASSERT_EQUALS( "void f ( ) { int i ; i = 0 ; if ( x ) { if ( 0 ) { i = 0 ; } } }", simplifyKnownVariables(code)); } void simplifyKnownVariables21() { const char code[] = "void foo() { int n = 10; for (int i = 0; i < n; ++i) { } }"; ASSERT_EQUALS( "void foo ( ) { int n ; n = 10 ; for ( int i = 0 ; i < 10 ; ++ i ) { } }", simplifyKnownVariables(code)); ASSERT_EQUALS( "void foo ( int i ) { int n ; n = i ; for ( i = 0 ; i < n ; ++ i ) { } }", simplifyKnownVariables("void foo(int i) { int n = i; for (i = 0; i < n; ++i) { } }")); } void simplifyKnownVariables22() { // This testcase is related to ticket #1169 { const char code[] = "void foo()\n" "{\n" " int n = 10;\n" " i = (n >> 1);\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int n ; n = 10 ; i = 10 >> 1 ; }", simplifyKnownVariables(code)); } { const char code[] = "void foo()\n" "{\n" " int n = 10;\n" " i = (n << 1);\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int n ; n = 10 ; i = 10 << 1 ; }", simplifyKnownVariables(code)); } { const char code[] = "void foo()\n" "{\n" " int n = 10;\n" " i = (1 << n);\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int n ; n = 10 ; i = 1 << 10 ; }", simplifyKnownVariables(code)); } { const char code[] = "void foo()\n" "{\n" " int n = 10;\n" " i = (1 >> n);\n" "}\n"; ASSERT_EQUALS( "void foo ( ) { int n ; n = 10 ; i = 1 >> 10 ; }", simplifyKnownVariables(code)); } } void simplifyKnownVariables23() { // This testcase is related to ticket #1596 const char code[] = "void foo(int x)\n" "{\n" " int a[10], c = 0;\n" " if (x) {\n" " a[c] = 0;\n" " c++;\n" " } else {\n" " a[c] = 0;\n" " }\n" "}\n"; TODO_ASSERT_EQUALS( "void foo ( int x ) " "{" " int a [ 10 ] ; int c ; c = 0 ;" " if ( x ) { a [ 0 ] = 0 ; c = 1 ; }" " else { a [ 0 ] = 0 ; } " "}", "void foo ( int x ) " "{" " int a [ 10 ] ; int c ; c = 0 ;" " if ( x ) { a [ 0 ] = 0 ; c ++ ; }" " else { a [ c ] = 0 ; } " "}", simplifyKnownVariables(code)); } void simplifyKnownVariables25() { { // This testcase is related to ticket #1646 const char code[] = "void foo(char *str)\n" "{\n" " int i;\n" " for (i=0;i<10;++i) {\n" " if (*str == 0) goto label;\n" " }\n" " return;\n" "label:\n" " str[i] = 0;\n" "}\n"; // Current result ASSERT_EQUALS( "void foo ( char * str ) " "{" " int i ;" " for ( i = 0 ; i < 10 ; ++ i ) {" " if ( * str == 0 ) { goto label ; }" " }" " return ;" " label : ;" " str [ i ] = 0 ; " "}", simplifyKnownVariables(code)); } { // This testcase is related to ticket #1646 const char code[] = "void foo(char *str)\n" "{\n" " int i;\n" " for (i=0;i<10;++i) { }\n" " return;\n" " str[i] = 0;\n" "}\n"; // Current result ASSERT_EQUALS( "void foo ( char * str ) " "{" " int i ;" " for ( i = 0 ; i < 10 ; ++ i ) { }" " return ;" " str [ i ] = 0 ; " "}", simplifyKnownVariables(code)); } } void simplifyKnownVariables27() { // This testcase is related to ticket #1633 const char code[] = "void foo()\n" "{\n" " int i1 = 1;\n" " int i2 = 2;\n" " int i3 = (i1 + i2) * 3;\n" "}\n"; ASSERT_EQUALS( "void foo ( ) " "{" " int i1 ; i1 = 1 ;" " int i2 ; i2 = 2 ;" " int i3 ; i3 = ( 1 + 2 ) * 3 ; " "}", simplifyKnownVariables(code)); } void simplifyKnownVariables28() { const char code[] = "void foo(int g)\n" "{\n" " int i = 2;\n" " if (g) {\n" " }\n" " if (i > 0) {\n" " }\n" "}\n"; ASSERT_EQUALS( "void foo ( int g ) " "{" " int i ; i = 2 ;" " if ( g ) { }" " if ( 2 > 0 ) { } " "}", simplifyKnownVariables(code)); } void simplifyKnownVariables29() { // ticket #1811 { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h + i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 + v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h - i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 - v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h * i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 * v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h / i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 / v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h & i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 & v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h | i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 | v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h ^ i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 ^ v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h % i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 % v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h >> i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 >> v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "int foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h << i;\n" "}\n"; const char expected[] = "1: int foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 << v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h == i;\n" "}\n"; const char expected[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 == v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h != i;\n" "}\n"; const char expected[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 != v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h > i;\n" "}\n"; const char expected[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 > v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h >= i;\n" "}\n"; const char expected[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 >= v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h < i;\n" "}\n"; const char expected[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 < v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h <= i;\n" "}\n"; const char expected[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 <= v@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h && i;\n" "}\n"; const char wanted[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 && v@2 ;\n" "6: }\n"; ASSERT_EQUALS(wanted, tokenizeDebugListing(code, true)); } { const char code[] = "bool foo(int u, int v)\n" "{\n" " int h = u;\n" " int i = v;\n" " return h || i;\n" "}\n"; const char wanted[] = "1: bool foo ( int u@1 , int v@2 )\n" "2: {\n" "3:\n" "4:\n" "5: return u@1 || v@2 ;\n" "6: }\n"; ASSERT_EQUALS(wanted, tokenizeDebugListing(code, true)); } } void simplifyKnownVariables30() { const char code[] = "int foo() {\n" " iterator it1 = ints.begin();\n" " iterator it2 = it1;\n" " for (++it2;it2!=ints.end();++it2);\n" "}\n"; const char expected[] = "int foo ( ) {\n" "iterator it1 ; it1 = ints . begin ( ) ;\n" "iterator it2 ; it2 = it1 ;\n" "for ( ++ it2 ; it2 != ints . end ( ) ; ++ it2 ) { ; }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables31() { const char code[] = "void foo(const char str[]) {\n" " const char *p = str;\n" " if (p[0] == 0) {\n" " }\n" "}\n"; const char expected[] = "void foo ( const char str [ ] ) {\n" "const char * p ; p = str ;\n" "if ( str [ 0 ] == 0 ) {\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables32() { { const char code[] = "void foo() {\n" " const int x = 0;\n" " bar(0,x);\n" "}\n"; const char expected[] = "void foo ( ) {\n\nbar ( 0 , 0 ) ;\n}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "static int const SZ = 22; char str[SZ];\n"; ASSERT_EQUALS("char str [ 22 ] ;", tokenizeAndStringify(code,true)); } } void simplifyKnownVariables33() { const char code[] = "static void foo(struct Foo *foo) {\n" " foo->a = 23;\n" " x[foo->a] = 0;\n" "}\n"; const char expected[] = "static void foo ( struct Foo * foo ) {\n" "foo . a = 23 ;\n" "x [ 23 ] = 0 ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables34() { const char code[] = "void f() {\n" " int x = 10;\n" " do { cin >> x; } while (x > 5);\n" " a[x] = 0;\n" "}\n"; const char expected[] = "void f ( ) {\n" "int x ; x = 10 ;\n" "do { cin >> x ; } while ( x > 5 ) ;\n" "a [ x ] = 0 ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables35() { // Ticket #2353 const char code[] = "int f() {" " int x = 0;" " if (x == 0) {" " return 0;" " }" " return 10 / x;" "}"; const char expected[] = "int f ( ) { int x ; x = 0 ; { return 0 ; } }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables36() { // Ticket #2304 const char code[] = "void f() {" " const char *q = \"hello\";" " strcpy(p, q);" "}"; const char expected[] = "void f ( ) { const char * q ; q = \"hello\" ; strcpy ( p , \"hello\" ) ; }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); // Ticket #5972 const char code2[] = "void f() {" " char buf[10] = \"ab\";" " memset(buf, 0, 10);" "}"; const char expected2[] = "void f ( ) { char buf [ 10 ] = \"ab\" ; memset ( buf , 0 , 10 ) ; }"; ASSERT_EQUALS(expected2, tokenizeAndStringify(code2, true)); } void simplifyKnownVariables37() { // Ticket #2398 - no simplification in for loop const char code[] = "void f() {\n" " double x = 0;\n" " for (int iter=0; iter<42; iter++) {\n" " int EvaldF = 1;\n" " if (EvaldF)\n" " Eval (x);\n" " }\n" "}"; const char expected[] = "void f ( ) {\n" "double x ; x = 0 ;\n" "for ( int iter = 0 ; iter < 42 ; iter ++ ) {\n" "\n" "\n" "Eval ( x ) ;\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables38() { // Ticket #2399 - simplify conditions const char code[] = "void f() {\n" " int x = 0;\n" " int y = 1;\n" " if (x || y);\n" "}"; const char expected[] = "void f ( ) {\n" "\n" "\n" ";\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables39() { // Ticket #2296 - simplify pointer alias 'delete p;' { const char code[] = "void f() {\n" " int *x;\n" " int *y = x;\n" " delete y;\n" "}"; ASSERT_EQUALS("void f ( ) {\nint * x ;\n\ndelete x ;\n}", tokenizeAndStringify(code, true)); } { const char code[] = "void f() {\n" " int *x;\n" " int *y = x;\n" " delete [] y;\n" "}"; ASSERT_EQUALS("void f ( ) {\nint * x ;\n\ndelete [ ] x ;\n}", tokenizeAndStringify(code, true)); } } void simplifyKnownVariables40() { const char code[] = "void f() {\n" " char c1 = 'a';\n" " char c2 = { c1 };\n" "}"; ASSERT_EQUALS("void f ( ) {\n\nchar c2 ; c2 = { 'a' } ;\n}", tokenizeAndStringify(code, true)); } void simplifyKnownVariables41() { const char code[] = "void f() {\n" " int x = 0;\n" " const int *p; p = &x;\n" " if (p) { return 0; }\n" "}"; ASSERT_EQUALS("void f ( ) {\nint x ; x = 0 ;\nconst int * p ; p = & x ;\nif ( & x ) { return 0 ; }\n}", tokenizeAndStringify(code, true)); } void simplifyKnownVariables42() { { const char code[] = "void f() {\n" " char str1[10], str2[10];\n" " strcpy(str1, \"abc\");\n" " strcpy(str2, str1);\n" "}"; const char expected[] = "void f ( ) {\n" "char str1 [ 10 ] ; char str2 [ 10 ] ;\n" "strcpy ( str1 , \"abc\" ) ;\n" "strcpy ( str2 , \"abc\" ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "void f() {\n" " char a[10];\n" " strcpy(a, \"hello\");\n" " strcat(a, \"!\");\n" "}"; const char expected[] = "void f ( ) {\n" "char a [ 10 ] ;\n" "strcpy ( a , \"hello\" ) ;\n" "strcat ( a , \"!\" ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Native, "test.c")); } { const char code[] = "void f() {" " char *s = malloc(10);" " strcpy(s, \"\");" " free(s);" "}"; const char expected[] = "void f ( ) {" " char * s ; s = malloc ( 10 ) ;" " strcpy ( s , \"\" ) ;" " free ( s ) ; " "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "void f(char *p, char *q) {" " strcpy(p, \"abc\");" " q = p;" "}"; const char expected[] = "void f ( char * p , char * q ) {" " strcpy ( p , \"abc\" ) ;" " q = p ; " "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } // 3538 { const char code[] = "void f() {\n" " char s[10];\n" " strcpy(s, \"123\");\n" " if (s[6] == ' ');\n" "}"; const char expected[] = "void f ( ) {\n" "char s [ 10 ] ;\n" "strcpy ( s , \"123\" ) ;\n" "if ( s [ 6 ] == ' ' ) { ; }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } } void simplifyKnownVariables43() { { const char code[] = "void f() {\n" " int a, *p; p = &a;\n" " { int a = *p; }\n" "}"; const char expected[] = "void f ( ) {\n" "int a ; int * p ; p = & a ;\n" "{ int a ; a = * p ; }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "void f() {\n" " int *a, **p; p = &a;\n" " { int *a = *p; }\n" "}"; const char expected[] = "void f ( ) {\n" "int * a ; int * * p ; p = & a ;\n" "{ int * a ; a = * p ; }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } } void simplifyKnownVariables44() { const char code[] = "void a() {\n" " static int i = 10;\n" " b(i++);\n" "}"; const char expected[] = "void a ( ) {\n" "static int i = 10 ;\n" "b ( i ++ ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables45() { const char code[] = "class Fred {\n" "private:\n" " const static int NUM = 2;\n" " int array[NUM];\n" "}"; const char expected[] = "class Fred {\n" "private:\n" "\n" "int array [ 2 ] ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariables46() { const char code[] = "void f() {\n" " int x = 0;\n" " cin >> x;\n" " return x;\n" "}"; { const char expected[] = "void f ( ) {\n" "int x ; x = 0 ;\n" "cin >> x ;\n" "return x ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Native, "test.cpp")); } { const char expected[] = "void f ( ) {\n" "\n" "cin >> 0 ;\n" "return 0 ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Native, "test.c")); } } void simplifyKnownVariables47() { // #3621 const char code[] = "void f() {\n" " int x = 0;\n" " cin >> std::hex >> x;\n" "}"; const char expected[] = "void f ( ) {\n" "int x ; x = 0 ;\n" "cin >> std :: hex >> x ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Native, "test.cpp")); } void simplifyKnownVariables48() { // #3754 const char code[] = "void f(int sz) {\n" " int i;\n" " for (i = 0; ((i3)); ++i) { }\n" "}"; const char expected[] = "void f ( int sz ) {\n" "int i ;\n" "for ( i = 0 ; ( i < sz ) && ( sz > 3 ) ; ++ i ) { }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Native, "test.c")); } void simplifyKnownVariables49() { // #3691 const char code[] = "void f(int sz) {\n" " switch (x) {\n" " case 1: sz = 2; continue;\n" " case 2: x = sz; break;\n" " }\n" "}"; const char expected[] = "void f ( int sz ) {\n" "switch ( x ) {\n" "case 1 : ; sz = 2 ; continue ;\n" "case 2 : ; x = sz ; break ;\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Native, "test.c")); } void simplifyKnownVariables50() { // #4066 { const char code[] = "void f() {\n" " char str1[10], str2[10];\n" " sprintf(str1, \"%%\");\n" " strcpy(str2, str1);\n" "}"; const char expected[] = "void f ( ) {\n" "char str1 [ 10 ] ; char str2 [ 10 ] ;\n" "sprintf ( str1 , \"%%\" ) ;\n" "strcpy ( str2 , \"%\" ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "void f() {\n" " char str1[25], str2[25];\n" " sprintf(str1, \"abcdef%%%% and %% and %\");\n" " strcpy(str2, str1);\n" "}"; const char expected[] = "void f ( ) {\n" "char str1 [ 25 ] ; char str2 [ 25 ] ;\n" "sprintf ( str1 , \"abcdef%%%% and %% and %\" ) ;\n" "strcpy ( str2 , \"abcdef%% and % and %\" ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { const char code[] = "void f() {\n" " char str1[10], str2[10];\n" " sprintf(str1, \"abc\");\n" " strcpy(str2, str1);\n" "}"; const char expected[] = "void f ( ) {\n" "char str1 [ 10 ] ; char str2 [ 10 ] ;\n" "sprintf ( str1 , \"abc\" ) ;\n" "strcpy ( str2 , \"abc\" ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } { //don't simplify '&x'! const char code[] = "const char * foo ( ) {\n" "const char x1 = 'b' ;\n" "f ( & x1 ) ;\n" "const char x2 = 'b' ;\n" "f ( y , & x2 ) ;\n" "const char x3 = 'b' ;\n" "t = & x3 ;\n" "const char x4 = 'b' ;\n" "t = y + & x4 ;\n" "const char x5 = 'b' ;\n" "z [ & x5 ] = y ;\n" "const char x6 = 'b' ;\n" "v = { & x6 } ;\n" "const char x7 = 'b' ;\n" "return & x7 ;\n" "}"; ASSERT_EQUALS(code, tokenizeAndStringify(code, true)); } { //don't simplify '&x'! const char code[] = "const int * foo ( ) {\n" "const int x1 = 1 ;\n" "f ( & x1 ) ;\n" "const int x2 = 1 ;\n" "f ( y , & x2 ) ;\n" "const int x3 = 1 ;\n" "t = & x3 ;\n" "const int x4 = 1 ;\n" "t = y + & x4 ;\n" "const int x5 = 1 ;\n" "z [ & x5 ] = y ;\n" "const int x6 = 1 ;\n" "v = { & x6 } ;\n" "const int x7 = 1 ;\n" "return & x7 ;\n" "}"; ASSERT_EQUALS(code, tokenizeAndStringify(code, true)); } } void simplifyKnownVariables51() { // #4409 hang const char code[] = "void mhz_M(int enough) {\n" " TYPE *x=&x, **p=x, **q = NULL;\n" " BENCH1(q = _mhz_M(n); n = 1;)\n" " use_pointer(q);\n" "}"; tokenizeAndStringify(code, true); // don't hang } void simplifyKnownVariables52() { // #4728 "= x %op%" ASSERT_EQUALS("void f ( ) { int y ; y = 34 + z ; }", tokenizeAndStringify("void f() { int x=34; int y=x+z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 - z ; }", tokenizeAndStringify("void f() { int x=34; int y=x-z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 * z ; }", tokenizeAndStringify("void f() { int x=34; int y=x*z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 / z ; }", tokenizeAndStringify("void f() { int x=34; int y=x/z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 % z ; }", tokenizeAndStringify("void f() { int x=34; int y=x%z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 & z ; }", tokenizeAndStringify("void f() { int x=34; int y=x&z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 | z ; }", tokenizeAndStringify("void f() { int x=34; int y=x|z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 ^ z ; }", tokenizeAndStringify("void f() { int x=34; int y=x^z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 << z ; }", tokenizeAndStringify("void f() { int x=34; int y=x<> z ; }", tokenizeAndStringify("void f() { int x=34; int y=x>>z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 && z ; }", tokenizeAndStringify("void f() { int x=34; int y=x&&z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 || z ; }", tokenizeAndStringify("void f() { int x=34; int y=x||z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 > z ; }", tokenizeAndStringify("void f() { int x=34; int y=x>z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 >= z ; }", tokenizeAndStringify("void f() { int x=34; int y=x>=z; }", true)); ASSERT_EQUALS("void f ( ) { int y ; y = 34 < z ; }", tokenizeAndStringify("void f() { int x=34; int y=xtype() == 1)); }", true)); } void simplifyKnownVariables53() { // references ASSERT_EQUALS("void f ( ) { int x ; x = abc ( ) ; }", tokenizeAndStringify("void f() { int x; int &ref=x; ref=abc(); }", true)); ASSERT_EQUALS("void f ( ) { int * p ; p = abc ( ) ; }", tokenizeAndStringify("void f() { int *p; int *&ref=p; ref=abc(); }", true)); } void simplifyKnownVariables54() { // #4913 ASSERT_EQUALS("void f ( int * p ) { * -- p = 0 ; * p = 0 ; }", tokenizeAndStringify("void f(int*p) { *--p=0; *p=0; }", true)); } void simplifyKnownVariables55() { // pointer alias ASSERT_EQUALS("void f ( ) { int a ; if ( a > 0 ) { } }", tokenizeAndStringify("void f() { int a; int *p=&a; if (*p>0) {} }", true)); ASSERT_EQUALS("void f ( ) { int a ; struct AB ab ; ab . a = & a ; if ( a > 0 ) { } }", tokenizeAndStringify("void f() { int a; struct AB ab; ab.a = &a; if (*ab.a>0) {} }", true)); ASSERT_EQUALS("void f ( ) { int a ; if ( x > a ) { } }", tokenizeAndStringify("void f() { int a; int *p=&a; if (x>*p) {} }", true)); } void simplifyKnownVariables56() { // ticket #5301 - >> ASSERT_EQUALS("void f ( ) { int a ; a = 0 ; int b ; b = 0 ; * p >> a >> b ; return a / b ; }", tokenizeAndStringify("void f() { int a=0,b=0; *p>>a>>b; return a/b; }", true)); } void simplifyKnownVariables57() { // #4724 ASSERT_EQUALS("unsigned long long x ; x = 9223372036854775808UL ;", tokenizeAndStringify("unsigned long long x = 1UL << 63 ;", true)); ASSERT_EQUALS("long long x ; x = -9223372036854775808L ;", tokenizeAndStringify("long long x = 1L << 63 ;", true)); } void simplifyKnownVariables58() { // #5268 const char code[] = "enum e { VAL1 = 1, VAL2 }; " "typedef char arr_t[VAL2]; " "int foo(int) ; " "void bar () { " " throw foo (VAL1); " "} " "int baz() { " " return sizeof(arr_t); " "}"; ASSERT_EQUALS("enum e { VAL1 = 1 , VAL2 } ; " "int foo ( int ) ; " "void bar ( ) { " "throw foo ( VAL1 ) ; " "} " "int baz ( ) { " "return sizeof ( char [ VAL2 ] ) ; " "}", tokenizeAndStringify(code, true)); } void simplifyKnownVariables59() { // #5062 - for head const char code[] = "void f() {\n" " int a[3], i, j;\n" " for(i = 0, j = 1; i < 3, j < 12; i++,j++) {\n" " a[i] = 0;\n" " }\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "int a [ 3 ] ; int i ; int j ;\n" "for ( i = 0 , j = 1 ; i < 3 , j < 12 ; i ++ , j ++ ) {\n" "a [ i ] = 0 ;\n" "}\n" "}", tokenizeAndStringify(code, true)); } void simplifyKnownVariables60() { // #6829 const char code[] = "void f() {\n" " int i = 1;\n" " const int * const constPtrToConst = &i;\n" " std::cout << *constPtrToConst << std::endl;\n" " std::cout << constPtrToConst << std::endl;\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "int i ; i = 1 ;\n" "const int * const constPtrToConst ; constPtrToConst = & i ;\n" "std :: cout << i << std :: endl ;\n" "std :: cout << & i << std :: endl ;\n" "}", tokenizeAndStringify(code, true)); } void simplifyKnownVariables61() { // #7805 tokenizeAndStringify("static const int XX = 0;\n" "enum E { XX };\n" "struct s {\n" " enum Bar {\n" " XX,\n" " Other\n" " };\n" " enum { XX };\n" "};", /*simplify=*/true); ASSERT_EQUALS("", errout.str()); } void simplifyKnownVariables62() { // #5666 ASSERT_EQUALS("void foo ( std :: string str ) {\n" "char * p ; p = & str [ 0 ] ;\n" "* p = 0 ;\n" "}", tokenizeAndStringify("void foo(std::string str) {\n" " char *p = &str[0];\n" " *p = 0;\n" "}", /*simplify=*/true)); } void simplifyKnownVariablesBailOutAssign1() { const char code[] = "int foo() {\n" " int i; i = 0;\n" " if (x) { i = 10; }\n" " return i;\n" "}\n"; const char expected[] = "int foo ( ) {\n" "int i ; i = 0 ;\n" "if ( x ) { i = 10 ; }\n" "return i ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariablesBailOutAssign2() { // ticket #3032 - assignment in condition const char code[] = "void f(struct ABC *list) {\n" " struct ABC *last = NULL;\n" " nr = (last = list->prev)->nr;\n" // <- don't replace "last" with 0 "}\n"; const char expected[] = "void f ( struct ABC * list ) {\n" "struct ABC * last ; last = NULL ;\n" "nr = ( last = list . prev ) . nr ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariablesBailOutAssign3() { // #4395 - nested assignments const char code[] = "void f() {\n" " int *p = 0;\n" " a = p = (VdbeCursor*)pMem->z;\n" " return p ;\n" "}\n"; const char expected[] = "void f ( ) {\n" "int * p ; p = 0 ;\n" "a = p = pMem . z ;\n" "return p ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariablesBailOutFor1() { const char code[] = "void foo() {\n" " for (int i = 0; i < 10; ++i) { }\n" "}\n"; const char expected[] = "void foo ( ) {\n" "for ( int i = 0 ; i < 10 ; ++ i ) { }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); // debug warnings } void simplifyKnownVariablesBailOutFor2() { const char code[] = "void foo() {\n" " int i = 0;\n" " while (i < 10) { ++i; }\n" "}\n"; const char expected[] = "void foo ( ) {\n" "int i ; i = 0 ;\n" "while ( i < 10 ) { ++ i ; }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); // debug warnings } void simplifyKnownVariablesBailOutFor3() { const char code[] = "void foo() {\n" " for (std::string::size_type pos = 0; pos < 10; ++pos)\n" " { }\n" "}\n"; const char expected[] = "void foo ( ) {\n" "for ( std :: string :: size_type pos = 0 ; pos < 10 ; ++ pos )\n" "{ }\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); // debug warnings } void simplifyKnownVariablesBailOutMemberFunction() { const char code[] = "void foo(obj a) {\n" " obj b = a;\n" " b.f();\n" "}\n"; const char expected[] = "void foo ( obj a ) {\n" "obj b ; b = a ;\n" "b . f ( ) ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void simplifyKnownVariablesBailOutConditionalIncrement() { const char code[] = "int f() {\n" " int a = 0;\n" " if (x) {\n" " ++a;\n" // conditional increment " }\n" " return a;\n" "}\n"; tokenizeAndStringify(code,true); ASSERT_EQUALS("", errout.str()); // no debug warnings } void simplifyKnownVariablesBailOutSwitchBreak() { // Ticket #2324 const char code[] = "int f(char *x) {\n" " char *p;\n" " char *q;\n" "\n" " switch (x & 0x3)\n" " {\n" " case 1:\n" " p = x;\n" " x = p;\n" " break;\n" " case 2:\n" " q = x;\n" // x is not equal with p " x = q;\n" " break;\n" " }\n" "}\n"; const char expected[] = "int f ( char * x ) {\n" "char * p ;\n" "char * q ;\n" "\n" "switch ( x & 3 )\n" "{\n" "case 1 : ;\n" "p = x ;\n" "x = p ;\n" "break ;\n" "case 2 : ;\n" "q = x ;\n" "x = q ;\n" "break ;\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } void simplifyKnownVariablesFloat() { // Ticket #2454 const char code[] = "void f() {\n" " float a = 40;\n" " x(10 / a);\n" "}\n"; const char expected[] = "void f ( ) {\n\nx ( 0.25 ) ;\n}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); // Ticket #4227 const char code2[] = "double f() {" " double a = false;" " return a;" "}"; ASSERT_EQUALS("double f ( ) { return 0.0 ; }", tokenizeAndStringify(code2,true)); // Ticket #5485 const char code3[] = "void f() {" " double a = 1e+007;\n" " std::cout << a;\n" "}"; ASSERT_EQUALS("void f ( ) {\nstd :: cout << 1e+007 ;\n}", tokenizeAndStringify(code3,true)); const char code4[] = "void f() {" " double a = 1;\n" " std::cout << a;\n" "}"; ASSERT_EQUALS("void f ( ) {\nstd :: cout << 1.0 ;\n}", tokenizeAndStringify(code4,true)); } void simplifyKnownVariablesFunctionCalls() { { const char code[] = "void a(int x);" // <- x is passed by value "void b() {" " int x = 123;" " a(x);" // <- replace with a(123); "}"; const char expected[] = "void a ( int x ) ; void b ( ) { a ( 123 ) ; }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } { const char code[] = "void a(int &x);" // <- x is passed by reference "void b() {" " int x = 123;" " a(x);" // <- don't replace with a(123); "}"; const char expected[] = "void a ( int & x ) ; void b ( ) { int x ; x = 123 ; a ( x ) ; }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } } void simplifyKnownVariablesGlobalVars() { // #8054 const char code[] = "static int x;" "void f() {" " x = 123;" " while (!x) { dostuff(); }" "}"; ASSERT_EQUALS("static int x ; void f ( ) { x = 123 ; while ( ! x ) { dostuff ( ) ; } }", tokenizeAndStringify(code,true)); } void simplifyKnownVariablesReturn() { const char code[] = "int a() {" " int x = 123;" " return (x);" "}"; ASSERT_EQUALS("int a ( ) { return 123 ; }", tokenizeAndStringify(code,true)); } void simplifyKnownVariablesPointerAliasFunctionCall() { // #7440 const char code[] = "int main() {\n" " char* data = new char[100];\n" " char** dataPtr = &data;\n" " printf(\"test\");\n" " delete [] *dataPtr;\n" "}"; const char exp[] = "int main ( ) {\n" "char * data ; data = new char [ 100 ] ;\n" "char * * dataPtr ; dataPtr = & data ;\n" "printf ( \"test\" ) ;\n" "delete [ ] data ;\n" "}"; ASSERT_EQUALS(exp, tokenizeAndStringify(code, /*simplify=*/true)); } void simplifyKnownVariablesClassMember() { // Ticket #2815 { const char code[] = "char *a;\n" "void f(const char *s) {\n" " a = NULL;\n" " x();\n" " memcpy(a, s, 10);\n" // <- don't simplify "a" here "}\n"; const std::string s(tokenizeAndStringify(code, true)); ASSERT_EQUALS(true, s.find("memcpy ( a , s , 10 ) ;") != std::string::npos); } // If the variable is local then perform simplification.. { const char code[] = "void f(const char *s) {\n" " char *a = NULL;\n" " x();\n" " memcpy(a, s, 10);\n" // <- simplify "a" "}\n"; const std::string s(tokenizeAndStringify(code, true)); TODO_ASSERT_EQUALS(true, false, s.find("memcpy ( 0 , s , 10 ) ;") != std::string::npos); } } void simplifyExternC() { ASSERT_EQUALS("int foo ( ) ;", tokenizeAndStringify("extern \"C\" int foo();")); ASSERT_EQUALS("int foo ( ) ;", tokenizeAndStringify("extern \"C\" { int foo(); }")); } void simplifyFunctionParameters() { { const char code[] = "char a [ ABC ( DEF ) ] ;"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } { const char code[] = "module ( a , a , sizeof ( a ) , 0444 ) ;"; ASSERT_EQUALS("module ( a , a , sizeof ( a ) , 292 ) ;", tokenizeAndStringify(code)); } ASSERT_EQUALS("void f ( int x ) { }", tokenizeAndStringify("void f(x) int x; { }")); ASSERT_EQUALS("void f ( int x , char y ) { }", tokenizeAndStringify("void f(x,y) int x; char y; { }")); ASSERT_EQUALS("int main ( int argc , char * argv [ ] ) { }", tokenizeAndStringify("int main(argc,argv) int argc; char *argv[]; { }")); ASSERT_EQUALS("int f ( int p , int w , float d ) { }", tokenizeAndStringify("int f(p,w,d) float d; { }")); // #1067 - Not simplified. Feel free to fix so it is simplified correctly but this syntax is obsolescent. ASSERT_EQUALS("int ( * d ( a , b , c ) ) ( ) int a ; int b ; int c ; { }", tokenizeAndStringify("int (*d(a,b,c))()int a,b,c; { }")); { // This is not a function but the pattern is similar.. const char code[] = "void foo()" "{" " if (x)" " int x;" " { }" "}"; ASSERT_EQUALS("void foo ( ) { if ( x ) { } { } }", tokenizeAndStringify(code, true)); } // #3770 - Don't segfault and don't change macro argument as if it's a K&R function argument { const char code[] = "MACRO(a)" "" "void f()" "{" " SetLanguage();" " {" " }" "}"; ASSERT_EQUALS("MACRO ( a ) void f ( ) { SetLanguage ( ) ; { } }", tokenizeAndStringify(code)); } } void simplifyFunctionParameters1() { // ticket #3721 const char code[] = "typedef float ufloat;\n" "typedef short ftnlen;\n" "int f(p,w,d,e,len) ufloat *p; ftnlen len;\n" "{\n" "}\n"; ASSERT_EQUALS("int f ( float * p , int w , int d , int e , short len )\n" "{\n" "}", tokenizeAndStringify(code)); } void simplifyFunctionParameters2() { // #4430 const char code[] = "class Item { " "int i ; " "public: " "Item ( int i ) ; " "} ; " "Item :: Item ( int i ) : i ( i ) { }"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } void simplifyFunctionParameters3() { // #4436 const char code[] = "class Item { " "int i ; " "int j ; " "public: " "Item ( int i , int j ) ; " "} ; " "Item :: Item ( int i , int j ) : i ( i ) , j ( j ) { }"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } void simplifyFunctionParametersErrors() { //same parameters... ASSERT_THROW(tokenizeAndStringify("void foo(x, x)\n" " int x;\n" " int x;\n" "{}\n"), InternalError); ASSERT_THROW(tokenizeAndStringify("void foo(x, y)\n" " int x;\n" " int x;\n" "{}\n"), InternalError); tokenizeAndStringify("void foo(int, int)\n" "{}\n"); ASSERT_EQUALS("", errout.str()); // #3848 - Don't hang tokenizeAndStringify("sal_Bool ShapeHasText(sal_uLong, sal_uLong) const {\n" " return sal_True;\n" "}\n" "void CreateSdrOLEFromStorage() {\n" " comphelper::EmbeddedObjectContainer aCnt( xDestStorage );\n" " { }\n" "}"); ASSERT_EQUALS("", errout.str()); } // Simplify "((..))" into "(..)" void removeParentheses1() { const char code[] = "void foo()" "{" " free(((void*)p));" "}"; ASSERT_EQUALS("void foo ( ) { free ( p ) ; }", tokenizeAndStringify(code, true)); } void removeParentheses3() { { const char code[] = "void foo()" "{" " if (( true )==(true)){}" "}"; ASSERT_EQUALS("void foo ( ) { }", tokenizeAndStringify(code, true)); } { const char code[] = "void foo()" "{" " if (( 2 )==(2)){}" "}"; ASSERT_EQUALS("void foo ( ) { }", tokenizeAndStringify(code, true)); } { const char code[] = "void foo()" "{" " if( g(10)){}" "}"; ASSERT_EQUALS("void foo ( ) { if ( g ( 10 ) ) { } }", tokenizeAndStringify(code)); } } // Simplify "( function (..))" into "function (..)" void removeParentheses4() { const char code[] = "void foo()" "{" " (free(p));" "}"; ASSERT_EQUALS("void foo ( ) { free ( p ) ; }", tokenizeAndStringify(code)); } void removeParentheses5() { // Simplify "( delete x )" into "delete x" { const char code[] = "void foo()" "{" " (delete p);" "}"; ASSERT_EQUALS("void foo ( ) { delete p ; }", tokenizeAndStringify(code)); } // Simplify "( delete [] x )" into "delete [] x" { const char code[] = "void foo()" "{" " (delete [] p);" "}"; ASSERT_EQUALS("void foo ( ) { delete [ ] p ; }", tokenizeAndStringify(code)); } } // "!(abc.a)" => "!abc.a" void removeParentheses6() { { const char code[] = "(!(abc.a));"; ASSERT_EQUALS("( ! abc . a ) ;", tokenizeAndStringify(code)); } //handle more complex member selections { const char code[] = "(!(a.b.c.d));"; ASSERT_EQUALS("( ! a . b . c . d ) ;", tokenizeAndStringify(code)); } } void removeParentheses7() { const char code[] = ";char *p; (delete(p), (p)=0);"; ASSERT_EQUALS("; char * p ; delete p ; p = 0 ;", tokenizeAndStringify(code,true)); } void removeParentheses8() { const char code[] = "struct foo {\n" " void operator delete(void *obj, size_t sz);\n" "}\n"; const std::string actual(tokenizeAndStringify(code, false, true, Settings::Win32A)); const char expected[] = "struct foo {\n" "void operatordelete ( void * obj , unsigned long sz ) ;\n" "}"; ASSERT_EQUALS(expected, actual); } void removeParentheses9() { ASSERT_EQUALS("void delete ( double num ) ;", tokenizeAndStringify("void delete(double num);", false)); } void removeParentheses10() { ASSERT_EQUALS("p = buf + 8 ;", tokenizeAndStringify("p = (buf + 8);", false)); } void removeParentheses11() { // #2502 ASSERT_EQUALS("{ } x ( ) ;", tokenizeAndStringify("{}(x());", false)); } void removeParentheses12() { // #2760 ASSERT_EQUALS(", x = 0 ;", tokenizeAndStringify(",(x)=0;", false)); } void removeParentheses13() { ASSERT_EQUALS("; f ( a + b , c ) ;", tokenizeAndStringify(";f((a+b),c);", false)); ASSERT_EQUALS("; x = y [ a + b ] ;", tokenizeAndStringify(";x=y[(a+b)];", false)); } void removeParentheses14() { ASSERT_EQUALS("; if ( ( i & 1 ) == 0 ) { ; } ;", tokenizeAndStringify("; if ( (i & 1) == 0 ); ;", false)); } void removeParentheses15() { ASSERT_EQUALS("a = b ? c : 123 ;", tokenizeAndStringify("a = b ? c : (123);", false)); ASSERT_EQUALS("a = b ? c : ( 579 ) ;", tokenizeAndStringify("a = b ? c : ((123)+(456));", false)); ASSERT_EQUALS("a = b ? 123 : c ;", tokenizeAndStringify("a = b ? (123) : c;", false)); // #4316 ASSERT_EQUALS("a = b ? c : ( d = 1 , 0 ) ;", tokenizeAndStringify("a = b ? c : (d=1,0);", false)); } void removeParentheses16() { // *(x.y)= // #4423 ASSERT_EQUALS("; * x = 0 ;", tokenizeAndStringify(";*(x)=0;", false)); ASSERT_EQUALS("; * x . y = 0 ;", tokenizeAndStringify(";*(x.y)=0;", false)); } void removeParentheses17() { // a ? b : (c > 0 ? d : e) ASSERT_EQUALS("a ? b : ( c > 0 ? d : e ) ;", tokenizeAndStringify("a?b:(c>0?d:e);", false)); } void removeParentheses18() { ASSERT_EQUALS("float ( * a ) [ 2 ] ;", tokenizeAndStringify("float(*a)[2];", false)); } void removeParentheses19() { ASSERT_EQUALS("( ( ( typeof ( X ) ) * ) 0 ) ;", tokenizeAndStringify("(((typeof(X))*)0);", false)); } void removeParentheses20() { ASSERT_EQUALS("a < b < int > > ( 2 ) ;", tokenizeAndStringify("a>(2);", false)); } void removeParentheses21() { ASSERT_EQUALS("a = ( int ) - b ;", tokenizeAndStringify("a = ((int)-b);", false)); } void removeParentheses22() { static char code[] = "struct S { " "char *(a); " "char &(b); " "const static char *(c); " "} ;"; static char exp[] = "struct S { " "char * a ; " "char & b ; " "static const char * c ; " "} ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } void removeParentheses23() { // Ticket #6103 // Reported case { static char code[] = "; * * p f ( ) int = { new int ( * [ 2 ] ) ; void }"; static char exp[] = "; * * p f ( ) int = { new int ( * [ 2 ] ) ; void }"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } // Various valid cases { static char code[] = "int * f [ 1 ] = { new ( int ) } ;"; static char exp[] = "int * f [ 1 ] = { new int } ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } { static char code[] = "int * * f [ 1 ] = { new ( int ) [ 1 ] } ;"; static char exp[] = "int * * f [ 1 ] = { new int [ 1 ] } ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } { static char code[] = "list < int > * f [ 1 ] = { new ( list < int > ) } ;"; static char exp[] = "list < int > * f [ 1 ] = { new list < int > } ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } // don't remove parentheses in operator new overload { static char code[] = "void *operator new(__SIZE_TYPE__, int);"; static char exp[] = "void * operatornew ( __SIZE_TYPE__ , int ) ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } } void removeParentheses24() { // Ticket #7040 static char code[] = "std::hash()(t._data);"; static char exp[] = "std :: hash < decltype ( t . _data ) > ( ) ( t . _data ) ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } void tokenize_double() { const char code[] = "void f() {\n" " double a = 4.2;\n" " float b = 4.2f;\n" " double c = 4.2e+10;\n" " double d = 4.2e-10;\n" " int e = 4+2;\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "double a ; a = 4.2 ;\n" "float b ; b = 4.2f ;\n" "double c ; c = 4.2e+10 ;\n" "double d ; d = 4.2e-10 ;\n" "int e ; e = 6 ;\n" "}", tokenizeAndStringify(code)); } void tokenize_strings() { const char code[] = "void f() {\n" "const char *a =\n" "{\n" "\"hello \"\n" "\"more \"\n" "\"world\"\n" "};\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "const char * a ; a =\n" "{\n" "\"hello more world\"\n" "\n" "\n" "} ;\n" "}", tokenizeAndStringify(code)); } void simplify_constants() { const char code[] = "void f() {\n" "const int a = 45;\n" "if( a )\n" "{ int b = a; }\n" "}\n" "void g() {\n" "int a = 2;\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "\n" "\n" "\n" "}\n" "void g ( ) {\n" "\n" "}", tokenizeAndStringify(code, true)); } void simplify_constants2() { const char code[] = "void f( Foo &foo, Foo *foo2 ) {\n" "const int a = 45;\n" "foo.a=a+a;\n" "foo2->a=a;\n" "}"; ASSERT_EQUALS("void f ( Foo & foo , Foo * foo2 ) {\n" "\n" "foo . a = 90 ;\n" "foo2 . a = 45 ;\n" "}", tokenizeAndStringify(code, true)); } void simplify_constants3() { const char code[] = "static const char str[] = \"abcd\";\n" "static const unsigned int SZ = sizeof(str);\n" "void f() {\n" "a = SZ;\n" "}\n"; const char expected[] = "static const char str [ 5 ] = \"abcd\" ;\n\nvoid f ( ) {\na = 5 ;\n}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } void simplify_constants4() { const char code[] = "static const int bSize = 4;\n" "static const int aSize = 50;\n" "x = bSize;\n" "y = aSize;\n"; ASSERT_EQUALS("x = 4 ;\ny = 50 ;", tokenizeAndStringify(code,true)); } void simplify_constants5() { const char code[] = "int buffer[10];\n" "static const int NELEMS = sizeof(buffer)/sizeof(int);\n" "static const int NELEMS2(sizeof(buffer)/sizeof(int));\n" "x = NELEMS;\n" "y = NELEMS2;\n"; ASSERT_EQUALS("int buffer [ 10 ] ;\n\n\nx = 10 ;\ny = 10 ;", tokenizeAndStringify(code,true)); } void simplify_constants6() { // Ticket #5625 { const char code[] = "template < class T > struct foo ;\n" "void bar ( ) {\n" "foo < 1 ? 0 ? 1 : 6 : 2 > x ;\n" "foo < 1 ? 0 : 2 > y ;\n" "}"; const char exp [] = "template < class T > struct foo ;\n" "void bar ( ) {\n" "foo < 6 > x ;\n" "foo < 0 > y ;\n" "}"; ASSERT_EQUALS(exp, tokenizeAndStringify(code, true)); } { const char code[] = "bool b = true ? false : 1 > 2 ;"; const char exp [] = "bool b ; b = false ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code, true)); } } void simplifyMulAndParens() { // (error) Resource leak const char code[] = "void f() {" " *&n1=open();" " *&(n2)=open();" " *(&n3)=open();" " *&*&n4=open();" " *&*&*&(n5)=open();" " *&*&(*&n6)=open();" " *&*(&*&n7)=open();" " *(&*&n8)=open();" " *&(*&*&(*&n9))=open();" " (n10) = open();" " ((n11)) = open();" " ((*&n12))=open();" " *(&(*&n13))=open();" " ((*&(*&n14)))=open();" " ((*&(*&n15)))+=10;" "}"; const char expected[] = "void f ( ) {" " n1 = open ( ) ;" " n2 = open ( ) ;" " n3 = open ( ) ;" " n4 = open ( ) ;" " n5 = open ( ) ;" " n6 = open ( ) ;" " n7 = open ( ) ;" " n8 = open ( ) ;" " n9 = open ( ) ;" " n10 = open ( ) ;" " n11 = open ( ) ;" " n12 = open ( ) ;" " n13 = open ( ) ;" " n14 = open ( ) ;" " n15 += 10 ; " "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void simplifyStructDecl() { const char code[] = "const struct A { int a; int b; } a;"; ASSERT_EQUALS("struct A { int a ; int b ; } ; const struct A a ;", tokenizeAndStringify(code)); } void vardecl1() { const char code[] = "unsigned int a, b;"; const std::string actual(tokenizeAndStringify(code)); ASSERT_EQUALS("unsigned int a ; unsigned int b ;", actual); } void vardecl2() { const char code[] = "void foo(a,b) unsigned int a, b; { }"; const std::string actual(tokenizeAndStringify(code)); ASSERT_EQUALS("void foo ( unsigned int a , unsigned int b ) { }", actual); } void vardecl3() { const char code[] = "void f() { char * p = foo<10,char>(); }"; const std::string actual(tokenizeAndStringify(code)); ASSERT_EQUALS("void f ( ) { char * p ; p = foo < 10 , char > ( ) ; }", actual); } void vardecl4() { // ticket #346 const char code1[] = "void *p = NULL;"; const char res1[] = "void * p ; p = NULL ;"; ASSERT_EQUALS(res1, tokenizeAndStringify(code1)); const char code2[] = "const void *p = NULL;"; const char res2[] = "const void * p ; p = NULL ;"; ASSERT_EQUALS(res2, tokenizeAndStringify(code2)); const char code3[] = "void * const p = NULL;"; const char res3[] = "void * const p ; p = NULL ;"; ASSERT_EQUALS(res3, tokenizeAndStringify(code3)); const char code4[] = "const void * const p = NULL;"; const char res4[] = "const void * const p ; p = NULL ;"; ASSERT_EQUALS(res4, tokenizeAndStringify(code4)); } void vardecl5() { ASSERT_EQUALS("void foo ( int nX ) {\n" "int addI ; addI = frontPoint == 2 || frontPoint == 1 ? ( i = 0 , 1 ) : ( i = nX - 2 , -1 ) ;\n" "}", tokenizeAndStringify("void foo(int nX) {\n" " int addI = frontPoint == 2 || frontPoint == 1 ? i = 0, 1 : (i = nX - 2, -1);\n" "}")); } void vardecl_stl_1() { // ticket #520 const char code1[] = "std::vectora, b;"; const char res1[] = "std :: vector < std :: string > a ; std :: vector < std :: string > b ;"; ASSERT_EQUALS(res1, tokenizeAndStringify(code1)); const char code2[] = "std::vector::const_iterator it, cit;"; const char res2[] = "std :: vector < std :: string > :: const_iterator it ; std :: vector < std :: string > :: const_iterator cit ;"; ASSERT_EQUALS(res2, tokenizeAndStringify(code2)); const char code3[] = "std::vector > *c, d;"; const char res3[] = "std :: vector < std :: pair < std :: string , std :: string > > * c ; std :: vector < std :: pair < std :: string , std :: string > > d ;"; ASSERT_EQUALS(res3, tokenizeAndStringify(code3)); } void vardecl_stl_2() { const char code1[] = "{ std::string x = \"abc\"; }"; ASSERT_EQUALS("{ std :: string x ; x = \"abc\" ; }", tokenizeAndStringify(code1)); const char code2[] = "{ std::vector x = y; }"; ASSERT_EQUALS("{ std :: vector < int > x ; x = y ; }", tokenizeAndStringify(code2)); } void vardecl_template_1() { // ticket #1046 const char code1[] = "b<(1<<24),10,24> u, v;"; const char res1[] = "b < 16777216 , 10 , 24 > u ; b < 16777216 , 10 , 24 > v ;"; ASSERT_EQUALS(res1, tokenizeAndStringify(code1)); // ticket #3571 (segmentation fault) tokenizeAndStringify("template 4) > class X4 {};"); } void vardecl_template_2() { // ticket #3650 const char code[] = "const string str = x<8,int>();"; const char expected[] = "const string str = x < 8 , int > ( ) ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void vardecl_union() { // ticket #1976 const char code1[] = "class Fred { public: union { int a ; int b ; } ; } ;"; ASSERT_EQUALS(code1, tokenizeAndStringify(code1)); // ticket #2039 const char code2[] = "void f() {\n" " union {\n" " int x;\n" " long y;\n" " };\n" "}"; ASSERT_EQUALS("void f ( ) {\n\nint x ;\nlong & y = x ;\n\n}", tokenizeAndStringify(code2)); // ticket #3927 const char code3[] = "union xy *p = NULL;"; ASSERT_EQUALS("union xy * p ; p = NULL ;", tokenizeAndStringify(code3)); } void vardecl_par() { // ticket #2743 - set links if variable type contains parentheses const char code[] = "Fred fred1=a, fred2=b;"; ASSERT_EQUALS("Fred < int ( * ) ( ) > fred1 ; fred1 = a ; Fred < int ( * ) ( ) > fred2 ; fred2 = b ;", tokenizeAndStringify(code)); } void vardecl_par2() { // ticket #3912 - set correct links const char code[] = "function)> v;"; ASSERT_EQUALS("function < void ( shared_ptr < MyClass > ) > v ;", tokenizeAndStringify(code)); } void vardecl_par3() { // ticket #6556- Fred x1(a), x2(b); const char code[] = "Fred x1(a), x2(b);"; ASSERT_EQUALS("Fred x1 ( a ) ; Fred x2 ( b ) ;", tokenizeAndStringify(code)); } void vardecl_class_ref() { const char code[] = "class A { B &b1,&b2; };"; ASSERT_EQUALS("class A { B & b1 ; B & b2 ; } ;", tokenizeAndStringify(code)); } void vardec_static() { { // don't simplify declarations of static variables // "static int i = 0;" is not the same as "static int i; i = 0;" const char code[] = "static int i = 0 ;"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } { const char code[] = "static int a, b;"; ASSERT_EQUALS("static int a ; static int b ;", tokenizeAndStringify(code)); } { const char code[] = "static unsigned int a, b;"; ASSERT_EQUALS("static unsigned int a ; static unsigned int b ;", tokenizeAndStringify(code)); } { const char code[] = "static int a=1, b=1;"; ASSERT_EQUALS("static int a = 1 ; static int b = 1 ;", tokenizeAndStringify(code)); } { const char code[] = "static int *a, *b;"; ASSERT_EQUALS("static int * a ; static int * b ;", tokenizeAndStringify(code)); } { const char code[] = "static unsigned int *a=0, *b=0;"; ASSERT_EQUALS("static unsigned int * a = 0 ; static unsigned int * b = 0 ;", tokenizeAndStringify(code)); } { // Ticket #4450 const char code[] = "static int large_eeprom_type = (13 | (5)), " "default_flash_type = 42;"; ASSERT_EQUALS("static int large_eeprom_type = 13 ; static int default_flash_type = 42 ;", tokenizeAndStringify(code)); } { // Ticket #5121 const char code[] = "unsigned int x;" "static const unsigned int A = 1, B = A, C = 0, D = (A), E = 0;" "void f() {" " unsigned int *foo = &x;" "}"; ASSERT_EQUALS("unsigned int x ; " "static const unsigned int A = 1 ; " "static const unsigned int B = A ; " "static const unsigned int C = 0 ; " "static const unsigned int D = A ; " "static const unsigned int E = 0 ; " "void f ( ) { " "unsigned int * foo ; " "foo = & x ; " "}", tokenizeAndStringify(code)); } { // Ticket #5266 const char code[] = "class Machine {\n" " static int const STACK_ORDER = 10, STACK_MAX = 1 << STACK_ORDER," " STACK_GUARD = 2;\n" "};"; ASSERT_EQUALS("class Machine {\n" "static const int STACK_ORDER = 10 ; static const int STACK_MAX = 1 << STACK_ORDER ; " "static const int STACK_GUARD = 2 ;\n" "} ;", tokenizeAndStringify(code)); } } void vardecl6() { // ticket #565 const char code1[] = "int z = x >> 16;"; const char res1[] = "int z ; z = x >> 16 ;"; ASSERT_EQUALS(res1, tokenizeAndStringify(code1)); } void vardecl7() { // ticket #603 const char code[] = "void f() {\n" " for (int c = 0; c < 0; ++c) {}\n" " int t;\n" " D(3 > t, \"T\");\n" "}"; const char res[] = "void f ( ) {\n" "for ( int c = 0 ; c < 0 ; ++ c ) { }\n" "int t ;\n" "D ( 3 > t , \"T\" ) ;\n" "}"; ASSERT_EQUALS(res, tokenizeAndStringify(code)); } void vardecl8() { // ticket #696 const char code[] = "char a[10]={'\\0'}, b[10]={'\\0'};"; const char res[] = "char a [ 10 ] = { '\\0' } ; char b [ 10 ] = { '\\0' } ;"; ASSERT_EQUALS(res, tokenizeAndStringify(code)); } void vardecl9() { const char code[] = "char a[2] = {'A', '\\0'}, b[2] = {'B', '\\0'};"; const char res[] = "char a [ 2 ] = { 'A' , '\\0' } ; char b [ 2 ] = { 'B' , '\\0' } ;"; ASSERT_EQUALS(res, tokenizeAndStringify(code)); } void vardecl10() { // ticket #732 const char code[] = "char a [ 2 ] = { '-' } ; memset ( a , '-' , sizeof ( a ) ) ;"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } void vardecl11() { // ticket #1684 const char code[] = "char a[5][8], b[5][8];"; ASSERT_EQUALS("char a [ 5 ] [ 8 ] ; char b [ 5 ] [ 8 ] ;", tokenizeAndStringify(code)); } void vardecl12() { const char code[] = "struct A { public: B a, b, c, d; };"; ASSERT_EQUALS("struct A { public: B a ; B b ; B c ; B d ; } ;", tokenizeAndStringify(code)); } void vardecl13() { const char code[] = "void f() {\n" " int a = (x < y) ? 1 : 0;\n" "}"; ASSERT_EQUALS("void f ( ) {\nint a ; a = ( x < y ) ? 1 : 0 ;\n}", tokenizeAndStringify(code)); } void vardecl14() { const char code[] = "::std::tr1::shared_ptr pNum1, pNum2;\n"; ASSERT_EQUALS(":: std :: tr1 :: shared_ptr < int > pNum1 ; :: std :: tr1 :: shared_ptr < int > pNum2 ;", tokenizeAndStringify(code, false, false, Settings::Native, "test.cpp", false)); } void vardecl15() { const char code[] = "const char x[] = \"foo\", y[] = \"bar\";\n"; ASSERT_EQUALS("const char x [ 4 ] = \"foo\" ; const char y [ 4 ] = \"bar\" ;", tokenizeAndStringify(code)); } void vardecl16() { { const char code[] = "const a::b::g::h::l *x [] = foo(),y [][] = bar();\n"; ASSERT_EQUALS("const a :: b < c , d ( e ) , f > :: g :: h < i > :: l * x [ ] = foo ( ) ; " "const a :: b < c , d ( e ) , f > :: g :: h < i > :: l y [ ] [ ] = bar ( ) ;", tokenizeAndStringify(code)); } { const char code[] = "const ::b::g::h::l *x [] = foo(),y [][] = bar();\n"; ASSERT_EQUALS("const :: b < c , d ( e ) , f > :: g :: h < i > :: l * x [ ] = foo ( ) ; " "const :: b < c , d ( e ) , f > :: g :: h < i > :: l y [ ] [ ] = bar ( ) ;", tokenizeAndStringify(code)); } } void vardecl17() { const char code[] = "a < b > :: c :: d :: e < f > x = foo(), y = bar();\n"; ASSERT_EQUALS("a < b > :: c :: d :: e < f > x ; x = foo ( ) ; " "a < b > :: c :: d :: e < f > y ; y = bar ( ) ;", tokenizeAndStringify(code)); } void vardecl18() { const char code[] = "void f() {\n" " g((double)v1*v2, v3, v4);\n" "}\n"; ASSERT_EQUALS("void f ( ) {\n" "g ( ( double ) v1 * v2 , v3 , v4 ) ;\n" "}", tokenizeAndStringify(code)); } void vardecl19() { { const char code[] = "void func(in, r, m)\n" "int in;" "int r,m;" "{\n" "}\n"; ASSERT_EQUALS("void func (\n" "int in ,\n" "int r ,\n" "int m )\n" "{\n" "}", tokenizeAndStringify(code)); } { const char code[] = "void f(r,f)\n" "char *r;\n" "{\n" "}\n"; ASSERT_EQUALS("void f (\n" "char * r )\n" "\n" "{\n" "}", tokenizeAndStringify(code)); } { const char code[] = "void f(f)\n" "{\n" "}\n"; ASSERT_EQUALS("void f ( )\n" "{\n" "}", tokenizeAndStringify(code)); } { const char code[] = "void f(f,r)\n" "char *r;\n" "{\n" "}\n"; ASSERT_EQUALS("void f (\n" "char * r )\n" "\n" "{\n" "}", tokenizeAndStringify(code)); } { const char code[] = "void f(r,f,s)\n" "char *r;\n" "char *s;\n" "{\n" "}\n"; ASSERT_EQUALS("void f (\n" "char * r ,\n" "\n" "char * s )\n" "\n" "\n" "{\n" "}", tokenizeAndStringify(code)); } { const char code[] = "void f(r,s,t)\n" "char *r,*s,*t;\n" "{\n" "}\n"; ASSERT_EQUALS("void f (\n" "char * r ,\n" "char * s ,\n" "char * t )\n" "\n" "{\n" "}", tokenizeAndStringify(code)); } { const char code[] = "void f(a, b) register char *a, *b;\n" "{\n" "}\n"; ASSERT_EQUALS("void f ( char * a , char * b )\n" "{\n" "}", tokenizeAndStringify(code)); } } void vardecl20() { // #3700 const char code[] = "void a::b() const\n" "{\n" " register const int X = 0;\n" "}\n"; ASSERT_EQUALS("void a :: b ( ) const\n" "{\n" "const int X = 0 ;\n" "}", tokenizeAndStringify(code)); } void vardecl21() { // type in namespace // #4042 - a::b const *p = 0; const char code1[] = "void f() {\n" " a::b const *p = 0;\n" "}\n"; ASSERT_EQUALS("void f ( ) {\n" "const a :: b * p ; p = 0 ;\n" "}" , tokenizeAndStringify(code1)); // #4226 - ::a::b const *p = 0; const char code2[] = "void f() {\n" " ::a::b const *p = 0;\n" "}\n"; ASSERT_EQUALS("void f ( ) {\n" "const :: a :: b * p ; p = 0 ;\n" "}" , tokenizeAndStringify(code2)); } void vardecl22() { // #4211 - segmentation fault tokenizeAndStringify("A> >* p = 0;"); } void vardecl23() { // #4276 - segmentation fault ASSERT_THROW(tokenizeAndStringify("class a { protected : template < class int x = 1 ; public : int f ( ) ; }"), InternalError); } void vardecl24() { // #4187 - variable declaration within lambda function const char code1[] = "void f() {\n" " std::for_each(ints.begin(), ints.end(), [](int val)\n" " {\n" " int temp = 0;\n" " });\n" "}"; const char expected1[] = "void f ( ) {\n" "std :: for_each ( ints . begin ( ) , ints . end ( ) , [ ] ( int val )\n" "{\n" "int temp ; temp = 0 ;\n" "} ) ;\n" "}"; ASSERT_EQUALS(expected1, tokenizeAndStringify(code1)); const char code2[] = "void f(int j) {\n" " g( [](){int temp = 0;} , j );\n" "}"; const char expected2[] = "void f ( int j ) {\n" "g ( [ ] ( ) { int temp ; temp = 0 ; } , j ) ;\n" "}"; ASSERT_EQUALS(expected2, tokenizeAndStringify(code2)); } void vardecl25() { // #4799 - segmentation fault tokenizeAndStringify("void A::func(P g) const {}\n" "void A::a() {\n" " b = new d( [this]( const P & p) -> double { return this->func(p);} );\n" "}"); } void vardecl26() { // #5907 const char code[] = "extern int *new, obj, player;"; const char expected[] = "extern int * new ; extern int obj ; extern int player ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, false, true, Settings::Native, "test.c")); ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void vardecl27() { // #7850 const char code[] = "extern int foo(char);\n" "void* class(char c) {\n" " if (foo(c))\n" " return 0;\n" " return 0;\n" "}"; tokenizeAndStringify(code, /*simplify=*/false, /*expand=*/true, Settings::Native, "test.c"); } void volatile_variables() { const char code[] = "volatile int a=0;\n" "volatile int b=0;\n" "volatile int c=0;\n"; const std::string actual(tokenizeAndStringify(code)); ASSERT_EQUALS("int a ; a = 0 ;\nint b ; b = 0 ;\nint c ; c = 0 ;", actual); } void simplifyKeyword() { { const char code[] = "void f (int a [ static 5] );"; ASSERT_EQUALS("void f ( int a [ 5 ] ) ;", tokenizeAndStringify(code)); } { const char in1[] = "class Base {\n" " virtual int test() = 0;\n" "};\n" "class Derived : public Base {\n" " virtual int test() override final {\n" " for( int Index ( 0 ); Index < 16; ++ Index) { int stub = 0; }\n" " }\n" "};"; const char out1[] = "class Base {\n" "virtual int test ( ) = 0 ;\n" "} ;\n" "class Derived : public Base {\n" "virtual int test ( ) {\n" "for ( int Index ( 0 ) ; Index < 16 ; ++ Index ) { int stub ; stub = 0 ; }\n" "}\n" "} ;"; ASSERT_EQUALS(out1, tokenizeAndStringify(in1)); const char in2[] = "class Derived{\n" " virtual int test() final override;" "};"; const char out2[] = "class Derived {\n" "virtual int test ( ) override ; } ;"; ASSERT_EQUALS(out2, tokenizeAndStringify(in2)); const char in3[] = "class Derived{\n" " virtual int test() final override const;" "};"; const char out3[] = "class Derived {\n" "virtual int test ( ) override const ; } ;"; ASSERT_EQUALS(out3, tokenizeAndStringify(in3)); const char in4 [] = "struct B final : A { void foo(); };"; const char out4 [] = "struct B : A { void foo ( ) ; } ;"; ASSERT_EQUALS(out4, tokenizeAndStringify(in4)); const char in5 [] = "struct ArrayItemsValidator final {\n" " SchemaError validate() const override {\n" " for (; pos < value.size(); ++pos) {\n" " }\n" " return none;\n" " }\n" "};\n"; const char out5 [] = "struct ArrayItemsValidator {\n" "SchemaError validate ( ) const override {\n" "for ( ; pos < value . size ( ) ; ++ pos ) {\n" "}\n" "return none ;\n" "}\n" "} ;"; ASSERT_EQUALS(out5, tokenizeAndStringify(in5)); } } /** * tokenize "signed i" => "signed int i" */ void signed1() { { const char code1[] = "void foo ( signed int , float ) ;"; ASSERT_EQUALS(code1, tokenizeAndStringify(code1)); } { const char code1[] = "signed i ;"; const char code2[] = "signed int i ;"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } { const char code1[] = "signed int i ;"; ASSERT_EQUALS(code1, tokenizeAndStringify(code1)); } { const char code1[] = "int signed i ;"; const char code2[] = "signed int i ;"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } { const char code1[] = "void f() { for (signed i=0; i<10; i++) {} }"; const char code2[] = "void f ( ) { for ( signed int i = 0 ; i < 10 ; i ++ ) { } }"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } } /** * tokenize "unsigned i" => "unsigned int i" * tokenize "unsigned" => "unsigned int" */ void unsigned1() { // No changes.. { const char code[] = "void foo ( unsigned int , float ) ;"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } // insert "int" after "unsigned".. { const char code1[] = "unsigned i ;"; const char code2[] = "unsigned int i ;"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } { const char code1[] = "int unsigned i ;"; const char code2[] = "unsigned int i ;"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } // insert "int" after "unsigned".. { const char code1[] = "void f() { for (unsigned i=0; i<10; i++) {} }"; const char code2[] = "void f ( ) { for ( unsigned int i = 0 ; i < 10 ; i ++ ) { } }"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } // "extern unsigned x;" => "extern int x;" { const char code1[] = "; extern unsigned x;"; const char code2[] = "; extern unsigned int x ;"; ASSERT_EQUALS(code2, tokenizeAndStringify(code1)); } } void unsigned2() { const char code[] = "i = (unsigned)j;"; const char expected[] = "i = ( unsigned int ) j ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } // simplify "unsigned" when using templates.. void unsigned3() { { const char code[] = "; foo();"; const char expected[] = "; foo ( ) ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "; foo();"; const char expected[] = "; foo ( ) ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } } void simplifyStdType() { // #4947, #4950, #4951 // usigned long long { const char code[] = "long long unsigned int x;"; const char expected[] = "unsigned long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "long long int unsigned x;"; const char expected[] = "unsigned long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "unsigned long long int x;"; const char expected[] = "unsigned long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "unsigned int long long x;"; const char expected[] = "unsigned long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int unsigned long long x;"; const char expected[] = "unsigned long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int long long unsigned x;"; const char expected[] = "unsigned long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } // signed long long { const char code[] = "long long signed int x;"; const char expected[] = "signed long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "long long int signed x;"; const char expected[] = "signed long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "signed long long int x;"; const char expected[] = "signed long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "signed int long long x;"; const char expected[] = "signed long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int signed long long x;"; const char expected[] = "signed long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int long long signed x;"; const char expected[] = "signed long long x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } // usigned short { const char code[] = "short unsigned int x;"; const char expected[] = "unsigned short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "short int unsigned x;"; const char expected[] = "unsigned short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "unsigned short int x;"; const char expected[] = "unsigned short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "unsigned int short x;"; const char expected[] = "unsigned short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int unsigned short x;"; const char expected[] = "unsigned short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int short unsigned x;"; const char expected[] = "unsigned short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } // signed short { const char code[] = "short signed int x;"; const char expected[] = "signed short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "short int signed x;"; const char expected[] = "signed short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "signed short int x;"; const char expected[] = "signed short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "signed int short x;"; const char expected[] = "signed short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int signed short x;"; const char expected[] = "signed short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "int short signed x;"; const char expected[] = "signed short x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "unsigned static short const int i;"; const char expected[] = "static const unsigned short i ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "float complex x;"; const char expected[] = "_Complex float x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "complex float x;"; const char expected[] = "_Complex float x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "complex long double x;"; const char expected[] = "_Complex long double x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "long double complex x;"; const char expected[] = "_Complex long double x ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } { const char code[] = "double complex;"; const char expected[] = "double complex ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } } void createLinks() { { const char code[] = "class A{\n" " void f() {}\n" "};"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // A body {} ASSERT_EQUALS(true, tok->linkAt(2) == tok->tokAt(9)); ASSERT_EQUALS(true, tok->linkAt(9) == tok->tokAt(2)); // f body {} ASSERT_EQUALS(true, tok->linkAt(7) == tok->tokAt(8)); ASSERT_EQUALS(true, tok->linkAt(8) == tok->tokAt(7)); // f () ASSERT_EQUALS(true, tok->linkAt(5) == tok->tokAt(6)); ASSERT_EQUALS(true, tok->linkAt(6) == tok->tokAt(5)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f(){\n" " char a[10];\n" " char *b ; b = new char[a[0]];\n" "};"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // a[10] ASSERT_EQUALS(true, tok->linkAt(7) == tok->tokAt(9)); ASSERT_EQUALS(true, tok->linkAt(9) == tok->tokAt(7)); // new char[] ASSERT_EQUALS(true, tok->linkAt(19) == tok->tokAt(24)); ASSERT_EQUALS(true, tok->linkAt(24) == tok->tokAt(19)); // a[0] ASSERT_EQUALS(true, tok->linkAt(21) == tok->tokAt(23)); ASSERT_EQUALS(true, tok->linkAt(23) == tok->tokAt(21)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f(){\n" " foo(g());\n" "};"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // foo( ASSERT_EQUALS(true, tok->linkAt(6) == tok->tokAt(10)); ASSERT_EQUALS(true, tok->linkAt(10) == tok->tokAt(6)); // g( ASSERT_EQUALS(true, tok->linkAt(8) == tok->tokAt(9)); ASSERT_EQUALS(true, tok->linkAt(9) == tok->tokAt(8)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "bool foo(C a, bar>& f, int b) {\n" " return(af);\n" "}"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // template< ASSERT_EQUALS(true, tok->tokAt(6) == tok->linkAt(4)); ASSERT_EQUALS(true, tok->tokAt(4) == tok->linkAt(6)); // bar< ASSERT_EQUALS(true, tok->tokAt(17) == tok->linkAt(10)); ASSERT_EQUALS(true, tok->tokAt(10) == tok->linkAt(17)); // x< ASSERT_EQUALS(true, tok->tokAt(16) == tok->linkAt(14)); ASSERT_EQUALS(true, tok->tokAt(14) == tok->linkAt(16)); // af ASSERT_EQUALS(true, 0 == tok->linkAt(28)); ASSERT_EQUALS(true, 0 == tok->linkAt(32)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void foo() {\n" " return static_cast(a);\n" "}"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // static_cast< ASSERT_EQUALS(true, tok->tokAt(9) == tok->linkAt(7)); ASSERT_EQUALS(true, tok->tokAt(7) == tok->linkAt(9)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void foo() {\n" " nvwa<(x > y)> ERROR_nnn;\n" "}"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // nvwa<(x > y)> ASSERT_EQUALS(true, tok->tokAt(12) == tok->linkAt(6)); ASSERT_EQUALS(true, tok->tokAt(6) == tok->linkAt(12)); ASSERT_EQUALS("", errout.str()); } { // #4860 const char code[] = "class A : public B {};"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); // B<..> ASSERT_EQUALS(true, tok->tokAt(5) == tok->linkAt(7)); ASSERT_EQUALS(true, tok->linkAt(5) == tok->tokAt(7)); ASSERT_EQUALS("", errout.str()); } { // #4860 const char code[] = "Bar>>>::set(1, 2, 3);"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->tokAt(1) == tok->linkAt(18)); ASSERT_EQUALS(true, tok->tokAt(3) == tok->linkAt(17)); ASSERT_EQUALS(true, tok->tokAt(7) == tok->linkAt(16)); ASSERT_EQUALS(true, tok->tokAt(11) == tok->linkAt(15)); ASSERT_EQUALS("", errout.str()); } { // #5627 const char code[] = "new Foo[10];"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->tokAt(2) == tok->linkAt(4)); ASSERT_EQUALS(true, tok->tokAt(4) == tok->linkAt(2)); ASSERT_EQUALS(true, tok->tokAt(5) == tok->linkAt(7)); ASSERT_EQUALS(true, tok->tokAt(7) == tok->linkAt(5)); ASSERT_EQUALS("", errout.str()); } { // #6242 const char code[] = "func = integral_;"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->tokAt(3) == tok->linkAt(9)); ASSERT_EQUALS(true, tok->linkAt(3) == tok->tokAt(9)); ASSERT_EQUALS("", errout.str()); } { // if (a < b || c > d) { } const char code[] = "if (a < b || c > d);"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(3) == nullptr); } { // if (a < ... > d) { } const char code[] = "if (a < b || c == 3 || d > e);"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(3) == nullptr); } { // template const char code[] = "a d;"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(1) == tok->tokAt(7)); } { // template const char code[] = "a d;"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(1) == tok->tokAt(7)); } { const char code[] = "template < f = b || c > struct S;"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(1) == tok->tokAt(7)); ASSERT_EQUALS(true, tok->tokAt(1) == tok->linkAt(7)); } { const char code[] = "struct A : B {};"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(4) == tok->tokAt(8)); ASSERT_EQUALS(true, tok->tokAt(4) == tok->linkAt(8)); } { const char code[] = "Data;"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(1) == tok->tokAt(4)); ASSERT_EQUALS(true, tok->tokAt(1) == tok->linkAt(4)); } { // #6601 const char code[] = "template struct FuncType : FuncType { };"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(true, tok->linkAt(1) == tok->tokAt(4)); // ASSERT_EQUALS(true, tok->linkAt(7) == tok->tokAt(14)); // ASSERT_EQUALS(true, tok->linkAt(9) == tok->tokAt(11)); // (&) ASSERT_EQUALS(true, tok->linkAt(12) == tok->tokAt(13)); // () ASSERT_EQUALS(true, tok->linkAt(17) == tok->tokAt(21)); // ASSERT_EQUALS(true, tok->linkAt(19) == tok->tokAt(20)); // () ASSERT_EQUALS(true, tok->linkAt(22) == tok->tokAt(23)); // {} } } void createLinks2() { { // #7158 const char code[] = "enum { value = boost::mpl::at_c };"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "<"); ASSERT_EQUALS(true, tok->link() == tok->tokAt(4)); ASSERT_EQUALS(true, tok->linkAt(4) == tok); } { // #7865 const char code[] = "template \n" "struct CheckedDivOp< T, U, typename std::enable_if::value || std::is_floating_point::value>::type> {\n" "};\n"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok1 = Token::findsimplematch(tokenizer.tokens(), "struct")->tokAt(2); const Token *tok2 = Token::findsimplematch(tokenizer.tokens(), "{")->previous(); ASSERT_EQUALS(true, tok1->link() == tok2); ASSERT_EQUALS(true, tok2->link() == tok1); } { // #7975 const char code[] = "template X copy() {};\n"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok1 = Token::findsimplematch(tokenizer.tokens(), "< Y"); const Token *tok2 = Token::findsimplematch(tok1, "> copy"); ASSERT_EQUALS(true, tok1->link() == tok2); ASSERT_EQUALS(true, tok2->link() == tok1); } { // #8006 const char code[] = "C && a = b;"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok1 = tokenizer.tokens()->next(); const Token *tok2 = tok1->tokAt(2); ASSERT_EQUALS(true, tok1->link() == tok2); ASSERT_EQUALS(true, tok2->link() == tok1); } { // #8115 const char code[] = "void Test(C && c);"; errout.str(""); Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok1 = Token::findsimplematch(tokenizer.tokens(), "<"); const Token *tok2 = tok1->tokAt(2); ASSERT_EQUALS(true, tok1->link() == tok2); ASSERT_EQUALS(true, tok2->link() == tok1); } } void simplifyString() { errout.str(""); Tokenizer tokenizer(&settings0, this); ASSERT_EQUALS("\"abc\"", tokenizer.simplifyString("\"abc\"")); ASSERT_EQUALS("\"\n\"", tokenizer.simplifyString("\"\\xa\"")); ASSERT_EQUALS("\"3\"", tokenizer.simplifyString("\"\\x33\"")); ASSERT_EQUALS("\"33\"", tokenizer.simplifyString("\"\\x333\"")); ASSERT_EQUALS("\"a\"", tokenizer.simplifyString("\"\\x61\"")); ASSERT_EQUALS("\"\n1\"", tokenizer.simplifyString("\"\\0121\"")); ASSERT_EQUALS("\"3\"", tokenizer.simplifyString("\"\\x33\"")); ASSERT_EQUALS("\" 0\"", tokenizer.simplifyString("\"\\0400\"")); ASSERT_EQUALS("\"\\nhello\"", tokenizer.simplifyString("\"\\nhello\"")); ASSERT_EQUALS("\"aaa\"", tokenizer.simplifyString("\"\\x61\\x61\\x61\"")); ASSERT_EQUALS("\"\n1\n1\n1\"", tokenizer.simplifyString("\"\\0121\\0121\\0121\"")); ASSERT_EQUALS("\"\\\\x61\"", tokenizer.simplifyString("\"\\\\x61\"")); ASSERT_EQUALS("\"b\"", tokenizer.simplifyString("\"\\x62\"")); ASSERT_EQUALS("\" 7\"", tokenizer.simplifyString("\"\\0407\"")); // terminate a string at null character. ASSERT_EQUALS(std::string("\"a") + '\0' + "\"", tokenizer.simplifyString("\"a\\0\"")); } void simplifyConst() { ASSERT_EQUALS("void foo ( ) { const int x ; }", tokenizeAndStringify("void foo(){ int const x;}")); ASSERT_EQUALS("void foo ( ) { { } const long x ; }", tokenizeAndStringify("void foo(){ {} long const x;}")); ASSERT_EQUALS("void foo ( int b , const unsigned int x ) { }", tokenizeAndStringify("void foo(int b,unsigned const x){}")); ASSERT_EQUALS("void foo ( ) { bar ( ) ; const char x ; }", tokenizeAndStringify("void foo(){ bar(); char const x;}")); ASSERT_EQUALS("void foo ( const char x ) { }", tokenizeAndStringify("void foo(char const x){}")); ASSERT_EQUALS("void foo ( int b , const char x ) { }", tokenizeAndStringify("void foo(int b,char const x){}")); ASSERT_EQUALS("void foo ( ) { int * const x ; }", tokenizeAndStringify("void foo(){ int * const x;}")); ASSERT_EQUALS("const int foo ( ) ;", tokenizeAndStringify("int const foo ();")); ASSERT_EQUALS("const int x ;", tokenizeAndStringify("int const x;")); ASSERT_EQUALS("const unsigned int x ;", tokenizeAndStringify("unsigned const x;")); ASSERT_EQUALS("const struct X x ;", tokenizeAndStringify("struct X const x;")); } void switchCase() { ASSERT_EQUALS("void foo ( int i ) { switch ( i ) { case -1 : ; break ; } }", tokenizeAndStringify("void foo (int i) { switch(i) { case -1: break; } }")); //ticket #3227 ASSERT_EQUALS("void foo ( ) { switch ( n ) { label : ; case 1 : ; label1 : ; label2 : ; break ; } }", tokenizeAndStringify("void foo(){ switch (n){ label: case 1: label1: label2: break; }}")); } void simplifyPointerToStandardType() { // Pointer to standard type ASSERT_EQUALS("char buf [ 100 ] ; readlink ( path , buf , 99 ) ;", tokenizeAndStringify("char buf[100] ; readlink(path, &buf[0], 99);", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void foo ( char * c ) { if ( 1 == ( 1 & c [ 0 ] ) ) { } }", tokenizeAndStringify("void foo(char *c) { if (1==(1 & c[0])) {} }", false, true, Settings::Native, "test.c")); // Simplification of unknown type - C only ASSERT_EQUALS("foo data [ 100 ] ; something ( foo ) ;", tokenizeAndStringify("foo data[100]; something(&foo[0]);", false, true, Settings::Native, "test.c")); // C++: No pointer simplification ASSERT_EQUALS("foo data [ 100 ] ; something ( & foo [ 0 ] ) ;", tokenizeAndStringify("foo data[100]; something(&foo[0]);")); } void functionpointer1() { ASSERT_EQUALS("void * f ;", tokenizeAndStringify("void (*f)();")); ASSERT_EQUALS("void * * f ;", tokenizeAndStringify("void *(*f)();")); ASSERT_EQUALS("unsigned int * f ;", tokenizeAndStringify("unsigned int (*f)();")); ASSERT_EQUALS("unsigned int * * f ;", tokenizeAndStringify("unsigned int * (*f)();")); } void functionpointer2() { const char code[] = "typedef void (* PF)();" "void f1 ( ) { }" "PF pf = &f1;" "PF pfs[] = { &f1, &f1 };"; const char expected[] = "void f1 ( ) { } " "void * pf ; pf = & f1 ; " "void * pfs [ 2 ] = { & f1 , & f1 } ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void functionpointer3() { // Related with ticket #2873 const char code[] = "void f() {\n" "(void)(xy(*p)(0);)" "\n}"; const char expected[] = "void f ( ) {\n" "( void ) ( xy ( * p ) ( 0 ) ; )\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void functionpointer4() { const char code[] = "struct S\n" "{\n" " typedef void (*FP)();\n" " virtual FP getFP();\n" " virtual void execute();\n" "};\n" "void f() {\n" " int a[9];\n" "}\n"; const char expected[] = "1: struct S\n" "2: {\n" "3:\n" "4: virtual void * getFP ( ) ;\n" "5: virtual void execute ( ) ;\n" "6: } ;\n" "7: void f ( ) {\n" "8: int a@1 [ 9 ] ;\n" "9: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, false)); } void functionpointer5() { const char code[] = ";void (*fp[])(int a) = {0,0,0};"; const char expected[] = "1: ; void * fp@1 [ 3 ] = { 0 , 0 , 0 } ;\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, false)); } void functionpointer6() { const char code1[] = ";void (*fp(f))(int);"; const char expected1[] = "1: ; void * fp ( f ) ;\n"; // No varId - it could be a function ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); const char code2[] = ";std::string (*fp(f))(int);"; const char expected2[] = "1: ; std :: string * fp ( f ) ;\n"; ASSERT_EQUALS(expected2, tokenizeDebugListing(code2, false)); } void functionpointer7() { const char code1[] = "void (X::*y)();"; const char expected1[] = "1: void * y@1 ;\n"; ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); } void functionpointer8() { const char code1[] = "int (*f)() throw(int);"; const char expected1[] = "1: int * f@1 ;\n"; ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); } void functionpointer9() { // function call with function pointer const char code1[] = "int f() { (*f)(); }"; const char expected1[] = "1: int f ( ) { ( * f ) ( ) ; }\n"; ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); const char code2[] = "int f() { return (*f)(); }"; const char expected2[] = "1: int f ( ) { return ( * f ) ( ) ; }\n"; ASSERT_EQUALS(expected2, tokenizeDebugListing(code2, false)); const char code3[] = "int f() { throw (*f)(); }"; const char expected3[] = "1: int f ( ) { throw ( * f ) ( ) ; }\n"; ASSERT_EQUALS(expected3, tokenizeDebugListing(code3, false)); } void removeRedundantAssignment() { ASSERT_EQUALS("void f ( ) { }", tokenizeAndStringify("void f() { int *p, *q; p = q; }", true)); ASSERT_EQUALS("void f ( ) { }", tokenizeAndStringify("void f() { int *p = 0, *q; p = q; }", true)); ASSERT_EQUALS("int f ( int * x ) { return * x ; }", tokenizeAndStringify("int f(int *x) { return *x; }", true)); } void removedeclspec() { ASSERT_EQUALS("a b", tokenizeAndStringify("a __declspec ( dllexport ) b")); ASSERT_EQUALS("a b", tokenizeAndStringify("a _declspec ( dllexport ) b")); ASSERT_EQUALS("int a ;", tokenizeAndStringify("__declspec(thread) __declspec(align(32)) int a;")); ASSERT_EQUALS("int i ;", tokenizeAndStringify("__declspec(allocate(\"mycode\")) int i;")); ASSERT_EQUALS("struct IUnknown ;", tokenizeAndStringify("struct __declspec(uuid(\"00000000-0000-0000-c000-000000000046\")) IUnknown;")); ASSERT_EQUALS("__property int x [ ] ;", tokenizeAndStringify("__declspec(property(get=GetX, put=PutX)) int x[];")); } void removeattribute() { ASSERT_EQUALS("short array [ 3 ] ;", tokenizeAndStringify("short array[3] __attribute__ ((aligned));")); ASSERT_EQUALS("int x [ 2 ] ;", tokenizeAndStringify("int x[2] __attribute__ ((packed));")); ASSERT_EQUALS("int vecint ;", tokenizeAndStringify("int __attribute__((mode(SI))) __attribute__((vector_size (16))) vecint;")); // alternate spelling #5328 ASSERT_EQUALS("short array [ 3 ] ;", tokenizeAndStringify("short array[3] __attribute ((aligned));")); ASSERT_EQUALS("int x [ 2 ] ;", tokenizeAndStringify("int x[2] __attribute ((packed));")); ASSERT_EQUALS("int vecint ;", tokenizeAndStringify("int __attribute((mode(SI))) __attribute((vector_size (16))) vecint;")); ASSERT_EQUALS("struct Payload_IR_config { uint8_t tap [ 16 ] ; } ;", tokenizeAndStringify("struct __attribute__((packed, gcc_struct)) Payload_IR_config { uint8_t tap[16]; };")); } void functionAttributeBefore() { const char code[] = "void __attribute__((pure)) __attribute__((nothrow)) __attribute__((const)) func1();\n" "void __attribute__((__pure__)) __attribute__((__nothrow__)) __attribute__((__const__)) func2();\n" "void __attribute__((nothrow)) __attribute__((pure)) __attribute__((const)) func3();\n" "void __attribute__((__nothrow__)) __attribute__((__pure__)) __attribute__((__const__)) func4();\n" "void __attribute__((noreturn)) func5();"; const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ; void func5 ( ) ;"; errout.str(""); // tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Expected result.. ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(0, false)); const Token * func1 = Token::findsimplematch(tokenizer.tokens(), "func1"); const Token * func2 = Token::findsimplematch(tokenizer.tokens(), "func2"); const Token * func3 = Token::findsimplematch(tokenizer.tokens(), "func3"); const Token * func4 = Token::findsimplematch(tokenizer.tokens(), "func4"); const Token * func5 = Token::findsimplematch(tokenizer.tokens(), "func5"); ASSERT(func1 && func1->isAttributePure() && func1->isAttributeNothrow() && func1->isAttributeConst()); ASSERT(func2 && func2->isAttributePure() && func2->isAttributeNothrow() && func2->isAttributeConst()); ASSERT(func3 && func3->isAttributePure() && func3->isAttributeNothrow() && func3->isAttributeConst()); ASSERT(func4 && func4->isAttributePure() && func4->isAttributeNothrow() && func4->isAttributeConst()); ASSERT(func5 && func5->isAttributeNoreturn()); } void functionAttributeAfter() { const char code[] = "void func1() __attribute__((pure)) __attribute__((nothrow)) __attribute__((const));\n" "void func2() __attribute__((__pure__)) __attribute__((__nothrow__)) __attribute__((__const__));\n" "void func3() __attribute__((nothrow)) __attribute__((pure)) __attribute__((const));\n" "void func4() __attribute__((__nothrow__)) __attribute__((__pure__)) __attribute__((__const__));" "void func5() __attribute__((noreturn));"; const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ; void func5 ( ) ;"; errout.str(""); // tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Expected result.. ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(0, false)); const Token * func1 = Token::findsimplematch(tokenizer.tokens(), "func1"); const Token * func2 = Token::findsimplematch(tokenizer.tokens(), "func2"); const Token * func3 = Token::findsimplematch(tokenizer.tokens(), "func3"); const Token * func4 = Token::findsimplematch(tokenizer.tokens(), "func4"); const Token * func5 = Token::findsimplematch(tokenizer.tokens(), "func5"); ASSERT(func1 && func1->isAttributePure() && func1->isAttributeNothrow() && func1->isAttributeConst()); ASSERT(func2 && func2->isAttributePure() && func2->isAttributeNothrow() && func2->isAttributeConst()); ASSERT(func3 && func3->isAttributePure() && func3->isAttributeNothrow() && func3->isAttributeConst()); ASSERT(func4 && func4->isAttributePure() && func4->isAttributeNothrow() && func4->isAttributeConst()); ASSERT(func5 && func5->isAttributeNoreturn()); } void cpp03template1() { { const char *code = "template struct extent {};"; ASSERT_EQUALS("template < typename > struct extent { } ;", tokenizeAndStringify(code)); } { const char *code = "template struct extent;"; ASSERT_EQUALS("template < typename > struct extent ;", tokenizeAndStringify(code)); } { const char *code = "template struct extent;"; ASSERT_EQUALS("template < typename , unsigned int = 0 > struct extent ;", tokenizeAndStringify(code)); } } void cpp0xtemplate1() { const char *code = "template \n" "void fn2 (T t = []{return 1;}())\n" "{}\n" "int main()\n" "{\n" " fn2();\n" "}\n"; ASSERT_EQUALS("int main ( )\n{\nfn2 ( ) ;\n} void fn2 ( int t = [ ] { return 1 ; } ( ) )\n{ }", tokenizeAndStringify(code)); } void cpp0xtemplate2() { // tokenize ">>" into "> >" const char *code = "list> ints;\n"; ASSERT_EQUALS("list < list < int > > ints ;", tokenizeAndStringify(code)); } void cpp0xtemplate3() { // #2549 const char *code = "template\n" "struct S\n" "{};\n" "S s;\n"; TODO_ASSERT_EQUALS("S s ; struct S { } ;", // wanted result "template < class T , T t >\n" "struct S\n" "{ } ;\n" "S < int , ( T ) 0 > s ;", // current result tokenizeAndStringify(code)); } void cpp0xtemplate4() { // #6181, #6354, #6414 tokenizeAndStringify("class A; " "template class Disposer; " "template > class Shim {}; " "class B : public Shim {};"); tokenizeAndStringify("template class ELFObjectImage {}; " "ObjectImage *createObjectImage() { " " return new ELFObjectImage>(Obj); " "} " "void resolveX86_64Relocation() { " " reinterpret_cast(0); " "}"); tokenizeAndStringify("template " "value_type Base(const value_type x, const value_type dx, function_type func, int type_deriv) { " " return 0.0; " "}; " "namespace { " " template class C { " " void Fun(int G, const double x); " " }; " " template void C::Fun(int G, const double x) {" " Base>(2, 2, f, 0); " " }; " " template class C2 {}; " "}"); } void cpp14template() { // Ticket #6708 tokenizeAndStringify("template " "decltype(auto) forward(T& t) { return 0; }"); } void arraySize() { ASSERT_EQUALS("; int a [ 3 ] = { 1 , 2 , 3 } ;", tokenizeAndStringify(";int a[]={1,2,3};")); ASSERT_EQUALS("; int a [ 3 ] = { 1 , 2 , 3 } ;", tokenizeAndStringify(";int a[]={1,2,3,};")); ASSERT_EQUALS("; foo a [ 3 ] = { { 1 , 2 } , { 3 , 4 } , { 5 , 6 } } ;", tokenizeAndStringify(";foo a[]={{1,2},{3,4},{5,6}};")); ASSERT_EQUALS("; int a [ 1 ] = { foo < bar1 , bar2 > ( 123 , 4 ) } ;", tokenizeAndStringify(";int a[]={foo(123,4)};")); ASSERT_EQUALS("; int a [ 2 ] = { b > c ? 1 : 2 , 3 } ;", tokenizeAndStringify(";int a[]={ b>c?1:2,3};")); ASSERT_EQUALS("int main ( ) { int a [ 2 ] = { b < c ? 1 : 2 , 3 } }", tokenizeAndStringify("int main(){int a[]={bx)=0; }")); //with '(' parentheses ASSERT_EQUALS("void f ( ) { ab : ; * ( * b ) . x = 0 ; }", tokenizeAndStringify("void f() { ab: *(* b)->x=0; }")); ASSERT_EQUALS("void f ( ) { ab : ; ( * * b ) . x = 0 ; }", tokenizeAndStringify("void f() { ab: (** b).x=0; }")); ASSERT_EQUALS("void f ( ) { ab : ; & ( * b . x ) = 0 ; }", tokenizeAndStringify("void f() { ab: &(*b.x)=0; }")); //with '{' parentheses ASSERT_EQUALS("void f ( ) { ab : ; { b = 0 ; } }", tokenizeAndStringify("void f() { ab: {b=0;} }")); ASSERT_EQUALS("void f ( ) { ab : ; { * b = 0 ; } }", tokenizeAndStringify("void f() { ab: { *b=0;} }")); ASSERT_EQUALS("void f ( ) { ab : ; { & b = 0 ; } }", tokenizeAndStringify("void f() { ab: { &b=0;} }")); ASSERT_EQUALS("void f ( ) { ab : ; { & ( * b . x ) = 0 ; } }", tokenizeAndStringify("void f() { ab: {&(*b.x)=0;} }")); //with unhandled MACRO() code ASSERT_EQUALS("void f ( ) { MACRO ( ab : b = 0 ; , foo ) }", tokenizeAndStringify("void f() { MACRO(ab: b=0;, foo)}")); ASSERT_EQUALS("void f ( ) { MACRO ( bar , ab : { & ( * b . x ) = 0 ; } ) }", tokenizeAndStringify("void f() { MACRO(bar, ab: {&(*b.x)=0;})}")); } void simplifyInitVar() { { const char code[] = "int i ; int p(0);"; ASSERT_EQUALS("int i ; int p ; p = 0 ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int i; int *p(0);"; ASSERT_EQUALS("int i ; int * p ; p = 0 ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int p(0);"; ASSERT_EQUALS("int p ; p = 0 ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int *p(0);"; ASSERT_EQUALS("int * p ; p = 0 ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int i ; int p(i);"; ASSERT_EQUALS("int i ; int p ; p = i ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int i; int *p(&i);"; ASSERT_EQUALS("int i ; int * p ; p = & i ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int i; void *p(&i);"; ASSERT_EQUALS("int i ; void * p ; p = & i ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "struct S { }; struct S s; struct S *p(&s);"; ASSERT_EQUALS("struct S { } ; struct S s ; struct S * p ; p = & s ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "struct S { }; S s; S *p(&s);"; ASSERT_EQUALS("struct S { } ; S s ; S * p ; p = & s ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "union S { int i; float f; }; union S s; union S *p(&s);"; ASSERT_EQUALS("union S { int i ; float f ; } ; union S s ; union S * p ; p = & s ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "union S { int i; float f; }; S s; S *p(&s);"; ASSERT_EQUALS("union S { int i ; float f ; } ; S s ; S * p ; p = & s ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class C { }; class C c; class C *p(&c);"; ASSERT_EQUALS("class C { } ; class C c ; class C * p ; p = & c ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class C { }; C c; C *p(&c);"; ASSERT_EQUALS("class C { } ; C c ; C * p ; p = & c ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "struct S { }; struct S s; struct S s1(s);"; ASSERT_EQUALS("struct S { } ; struct S s ; struct S s1 ( s ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "struct S { }; S s; S s1(s);"; ASSERT_EQUALS("struct S { } ; S s ; S s1 ( s ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "struct S { }; struct S s; struct S s1(&s);"; ASSERT_EQUALS("struct S { } ; struct S s ; struct S s1 ( & s ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "struct S { }; S s; S s1(&s);"; ASSERT_EQUALS("struct S { } ; S s ; S s1 ( & s ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class S { int function(); };"; ASSERT_EQUALS("class S { int function ( ) ; } ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class S { int function(void); };"; ASSERT_EQUALS("class S { int function ( ) ; } ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class S { int function(int); };"; ASSERT_EQUALS("class S { int function ( int ) ; } ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int function(void);"; ASSERT_EQUALS("int function ( ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int function(int);"; ASSERT_EQUALS("int function ( int ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "extern int function(void);"; ASSERT_EQUALS("extern int function ( ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int function1(void); int function2(void);"; ASSERT_EQUALS("int function1 ( ) ; int function2 ( ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int function(A);"; // We can't tell if this a function prototype or a variable without knowing // what A is. Since A is undefined, just leave it alone. ASSERT_EQUALS("int function ( A ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int i; int function(A);"; ASSERT_EQUALS("int i ; int function ( A ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class A { } ; int foo(A);"; ASSERT_EQUALS("class A { } ; int foo ( A ) ;", tokenizeAndStringify(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "class A { } ; A a; int foo(a);"; ASSERT_EQUALS("class A { } ; A a ; int foo ; foo = a ;", tokenizeAndStringify(code, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "int x(f());"; ASSERT_EQUALS("int x ; x = f ( ) ;", tokenizeAndStringify(code, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "return doSomething(X), 0;"; ASSERT_EQUALS("return doSomething ( X ) , 0 ;", tokenizeAndStringify(code, false)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "const int x(1);" "const int y(2);" "const int z((x+1)*y);" "f(z);"; ASSERT_EQUALS("f ( 4 ) ;", tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "const int x(1);" "const int y(2);" "const int z((x+1)*y);" "f(&z);"; ASSERT_EQUALS("const int z ( 4 ) ; f ( & z ) ;", tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "const bool x(true);" "const bool y(!x);" "f(y);"; ASSERT_EQUALS("f ( false ) ;", tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "const bool x(true);" "const bool y(!x);" "f(&y);"; ASSERT_EQUALS("const bool y ( false ) ; f ( & y ) ;", tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); } } void simplifyInitVar2() { // ticket #5131 - unsigned const char code[] = "void f() {\n" " unsigned int a(0),b(0);\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "unsigned int a ; a = 0 ; unsigned int b ; b = 0 ;\n" "}", tokenizeAndStringify(code)); } void simplifyInitVar3() { const char code[] = "void f() {\n" " int *a(0),b(0);\n" "}"; ASSERT_EQUALS("void f ( ) {\n" "int * a ; a = 0 ; int b ; b = 0 ;\n" "}", tokenizeAndStringify(code)); } void bitfields1() { const char code1[] = "struct A { bool x : 1; };"; ASSERT_EQUALS("struct A { bool x ; } ;", tokenizeAndStringify(code1,false)); const char code2[] = "struct A { char x : 3; };"; ASSERT_EQUALS("struct A { char x ; } ;", tokenizeAndStringify(code2,false)); const char code3[] = "struct A { short x : 3; };"; ASSERT_EQUALS("struct A { short x ; } ;", tokenizeAndStringify(code3,false)); const char code4[] = "struct A { int x : 3; };"; ASSERT_EQUALS("struct A { int x ; } ;", tokenizeAndStringify(code4,false)); const char code5[] = "struct A { long x : 3; };"; ASSERT_EQUALS("struct A { long x ; } ;", tokenizeAndStringify(code5,false)); const char code6[] = "struct A { __int8 x : 3; };"; ASSERT_EQUALS("struct A { char x ; } ;", tokenizeAndStringifyWindows(code6,false, true, Settings::Win32A)); const char code7[] = "struct A { __int16 x : 3; };"; ASSERT_EQUALS("struct A { short x ; } ;", tokenizeAndStringifyWindows(code7,false, true, Settings::Win32A)); const char code8[] = "struct A { __int32 x : 3; };"; ASSERT_EQUALS("struct A { int x ; } ;", tokenizeAndStringifyWindows(code8,false, true, Settings::Win32A)); const char code9[] = "struct A { __int64 x : 3; };"; ASSERT_EQUALS("struct A { long long x ; } ;", tokenizeAndStringifyWindows(code9,false, true, Settings::Win32A)); const char code10[] = "struct A { unsigned char x : 3; };"; ASSERT_EQUALS("struct A { unsigned char x ; } ;", tokenizeAndStringify(code10,false)); const char code11[] = "struct A { unsigned short x : 3; };"; ASSERT_EQUALS("struct A { unsigned short x ; } ;", tokenizeAndStringify(code11,false)); const char code12[] = "struct A { unsigned int x : 3; };"; ASSERT_EQUALS("struct A { unsigned int x ; } ;", tokenizeAndStringify(code12,false)); const char code13[] = "struct A { unsigned long x : 3; };"; ASSERT_EQUALS("struct A { unsigned long x ; } ;", tokenizeAndStringify(code13,false)); const char code14[] = "struct A { unsigned __int8 x : 3; };"; ASSERT_EQUALS("struct A { unsigned char x ; } ;", tokenizeAndStringifyWindows(code14,false, true, Settings::Win32A)); const char code15[] = "struct A { unsigned __int16 x : 3; };"; ASSERT_EQUALS("struct A { unsigned short x ; } ;", tokenizeAndStringifyWindows(code15,false, true, Settings::Win32A)); const char code16[] = "struct A { unsigned __int32 x : 3; };"; ASSERT_EQUALS("struct A { unsigned int x ; } ;", tokenizeAndStringifyWindows(code16,false, true, Settings::Win32A)); const char code17[] = "struct A { unsigned __int64 x : 3; };"; ASSERT_EQUALS("struct A { unsigned long long x ; } ;", tokenizeAndStringifyWindows(code17,false, true, Settings::Win32A)); const char code18[] = "struct A { signed char x : 3; };"; ASSERT_EQUALS("struct A { signed char x ; } ;", tokenizeAndStringify(code18,false)); const char code19[] = "struct A { signed short x : 3; };"; ASSERT_EQUALS("struct A { signed short x ; } ;", tokenizeAndStringify(code19,false)); const char code20[] = "struct A { signed int x : 3; };"; ASSERT_EQUALS("struct A { signed int x ; } ;", tokenizeAndStringify(code20,false)); const char code21[] = "struct A { signed long x : 3; };"; ASSERT_EQUALS("struct A { signed long x ; } ;", tokenizeAndStringifyWindows(code21,false)); const char code22[] = "struct A { signed __int8 x : 3; };"; ASSERT_EQUALS("struct A { signed char x ; } ;", tokenizeAndStringifyWindows(code22,false, true, Settings::Win32A)); const char code23[] = "struct A { signed __int16 x : 3; };"; ASSERT_EQUALS("struct A { signed short x ; } ;", tokenizeAndStringifyWindows(code23,false, true, Settings::Win32A)); const char code24[] = "struct A { signed __int32 x : 3; };"; ASSERT_EQUALS("struct A { signed int x ; } ;", tokenizeAndStringifyWindows(code24,false, true, Settings::Win32A)); const char code25[] = "struct A { signed __int64 x : 3; };"; ASSERT_EQUALS("struct A { signed long long x ; } ;", tokenizeAndStringifyWindows(code25,false, true, Settings::Win32A)); } void bitfields2() { const char code1[] = "struct A { public: int x : 3; };"; ASSERT_EQUALS("struct A { public: int x ; } ;", tokenizeAndStringify(code1,false)); const char code2[] = "struct A { public: unsigned long x : 3; };"; ASSERT_EQUALS("struct A { public: unsigned long x ; } ;", tokenizeAndStringify(code2,false)); const char code3[] = "struct A { protected: int x : 3; };"; ASSERT_EQUALS("struct A { protected: int x ; } ;", tokenizeAndStringify(code3,false)); const char code4[] = "struct A { protected: unsigned long x : 3; };"; ASSERT_EQUALS("struct A { protected: unsigned long x ; } ;", tokenizeAndStringify(code4,false)); const char code5[] = "struct A { private: int x : 3; };"; ASSERT_EQUALS("struct A { private: int x ; } ;", tokenizeAndStringify(code5,false)); const char code6[] = "struct A { private: unsigned long x : 3; };"; ASSERT_EQUALS("struct A { private: unsigned long x ; } ;", tokenizeAndStringify(code6,false)); } void bitfields3() { const char code1[] = "struct A { const int x : 3; };"; ASSERT_EQUALS("struct A { const int x ; } ;", tokenizeAndStringify(code1,false)); const char code2[] = "struct A { const unsigned long x : 3; };"; ASSERT_EQUALS("struct A { const unsigned long x ; } ;", tokenizeAndStringify(code2,false)); const char code3[] = "struct A { public: const int x : 3; };"; ASSERT_EQUALS("struct A { public: const int x ; } ;", tokenizeAndStringify(code3,false)); const char code4[] = "struct A { public: const unsigned long x : 3; };"; ASSERT_EQUALS("struct A { public: const unsigned long x ; } ;", tokenizeAndStringify(code4,false)); } void bitfields4() { // ticket #1956 const char code1[] = "struct A { CHAR x : 3; };"; ASSERT_EQUALS("struct A { CHAR x ; } ;", tokenizeAndStringify(code1,false)); const char code2[] = "struct A { UCHAR x : 3; };"; ASSERT_EQUALS("struct A { UCHAR x ; } ;", tokenizeAndStringify(code2,false)); const char code3[] = "struct A { BYTE x : 3; };"; ASSERT_EQUALS("struct A { BYTE x ; } ;", tokenizeAndStringify(code3,false)); const char code4[] = "struct A { WORD x : 3; };"; ASSERT_EQUALS("struct A { WORD x ; } ;", tokenizeAndStringify(code4,false)); const char code5[] = "struct A { DWORD x : 3; };"; ASSERT_EQUALS("struct A { DWORD x ; } ;", tokenizeAndStringify(code5,false)); const char code6[] = "struct A { LONG x : 3; };"; ASSERT_EQUALS("struct A { LONG x ; } ;", tokenizeAndStringify(code6,false)); const char code7[] = "struct A { UINT8 x : 3; };"; ASSERT_EQUALS("struct A { UINT8 x ; } ;", tokenizeAndStringify(code7,false)); const char code8[] = "struct A { UINT16 x : 3; };"; ASSERT_EQUALS("struct A { UINT16 x ; } ;", tokenizeAndStringify(code8,false)); const char code9[] = "struct A { UINT32 x : 3; };"; ASSERT_EQUALS("struct A { UINT32 x ; } ;", tokenizeAndStringify(code9,false)); const char code10[] = "struct A { UINT64 x : 3; };"; ASSERT_EQUALS("struct A { UINT64 x ; } ;", tokenizeAndStringify(code10,false)); } void bitfields5() { // ticket #1956 const char code1[] = "struct RGB { unsigned int r : 3, g : 3, b : 2; };"; ASSERT_EQUALS("struct RGB { unsigned int r ; unsigned int g ; unsigned int b ; } ;", tokenizeAndStringify(code1,false)); const char code2[] = "struct A { int a : 3; int : 3; int c : 3; };"; ASSERT_EQUALS("struct A { int a ; int c ; } ;", tokenizeAndStringify(code2,false)); const char code3[] = "struct A { virtual void f() {} int f1 : 1; };"; ASSERT_EQUALS("struct A { virtual void f ( ) { } int f1 ; } ;", tokenizeAndStringify(code3,false)); } void bitfields6() { // ticket #2595 const char code1[] = "struct A { bool b : true; };"; ASSERT_EQUALS("struct A { bool b ; } ;", tokenizeAndStringify(code1,false)); const char code2[] = "struct A { bool b : true, c : true; };"; ASSERT_EQUALS("struct A { bool b ; bool c ; } ;", tokenizeAndStringify(code2,false)); const char code3[] = "struct A { bool : true; };"; ASSERT_EQUALS("struct A { } ;", tokenizeAndStringify(code3,false)); const char code4[] = "void f(int a) { switch (a) { case b: break; } }"; ASSERT_EQUALS("void f ( int a ) { switch ( a ) { case b : ; break ; } }", tokenizeAndStringify(code4,true)); const char code5[] = "void f(int a) { switch (a) { default: break; } }"; ASSERT_EQUALS("void f ( int a ) { switch ( a ) { default : ; break ; } }", tokenizeAndStringify(code5,true)); } void bitfields7() { // ticket #1987 const char code[] = "typedef struct Descriptor {" " unsigned element_size: 8* sizeof( unsigned );" "} Descriptor;"; const char expected[] = "struct Descriptor { " "unsigned int element_size ; " "} ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,false)); ASSERT_EQUALS("", errout.str()); } void bitfields8() { const char code[] = "struct A;" "class B : virtual public C" "{" " int f();" "};"; const char expected[] = "struct A ; " "class B : virtual public C " "{ " "int f ( ) ; " "} ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,false)); ASSERT_EQUALS("", errout.str()); } void bitfields9() { // ticket #2706 const char code[] = "void f() {\n" " goto half;\n" "half:\n" " {\n" " ;\n" " }\n" "};"; tokenizeAndStringify(code,false); ASSERT_EQUALS("", errout.str()); } void bitfields10() { // ticket #2737 const char code[] = "{}" "MACRO " "default: { }" ";"; ASSERT_EQUALS("{ } MACRO default : { } ;", tokenizeAndStringify(code,false)); } void bitfields12() { // ticket #3485 (segmentation fault) const char code[] = "{a:1;};\n"; ASSERT_EQUALS("{ } ;", tokenizeAndStringify(code,false)); } void bitfields13() { // ticket #3502 (segmentation fault) ASSERT_EQUALS("x y ;", tokenizeAndStringify("struct{x y:};\n",false)); } void bitfields14() { // #4561 - crash for 'signals:' ASSERT_EQUALS("class x { signals : } ;", tokenizeAndStringify("class x { signals: };\n",false)); } void bitfields15() { // #7747 - enum Foo {A,B}:4; ASSERT_EQUALS("struct AB {\n" "enum Foo { A , B } ; enum Foo Anonymous ;\n" "} ;", tokenizeAndStringify("struct AB {\n" " enum Foo {A,B} : 4;\n" "};")); ASSERT_EQUALS("struct AB {\n" "enum Foo { A , B } ; enum Foo foo ;\n" "} ;", tokenizeAndStringify("struct AB {\n" " enum Foo {A,B} foo : 4;\n" "};")); } void simplifyNamespaceStd() { static const char code1[] = "map m;"; // namespace std is not used ASSERT_EQUALS("map < foo , bar > m ;", tokenizeAndStringify(code1, false)); static const char code2[] = "using namespace std;\n" "map m;"; ASSERT_EQUALS("std :: map < foo , bar > m ;", tokenizeAndStringify(code2, false)); static const char code3[] = "using namespace std;\n" "string s;"; ASSERT_EQUALS("std :: string s ;", tokenizeAndStringify(code3, false)); static const char code4[] = "using namespace std;\n" "void foo() {swap(a, b); }"; ASSERT_EQUALS("void foo ( ) { std :: swap ( a , b ) ; }", tokenizeAndStringify(code4, false)); static const char code5[] = "using namespace std;\n" "void foo() {map(a, b); }"; // Thats obviously not std::map<> ASSERT_EQUALS("void foo ( ) { map ( a , b ) ; }", tokenizeAndStringify(code5, false)); static const char code6[] = "using namespace std;\n" "string s;"; // Thats obviously not std::string ASSERT_EQUALS("string < wchar_t > s ;", tokenizeAndStringify(code6, false)); static const char code7[] = "using namespace std;\n" "swap s;"; // Thats obviously not std::swap ASSERT_EQUALS("swap s ;", tokenizeAndStringify(code7, false)); static const char code8[] = "using namespace std;\n" "std::string s;"; ASSERT_EQUALS("std :: string s ;", tokenizeAndStringify(code8, false)); static const char code9[] = "using namespace std;\n" "tr1::function f;"; ASSERT_EQUALS("tr1 :: function < void ( int ) > f ;", tokenizeAndStringify(code9, false, true, Settings::Native, "test.cpp", false)); ASSERT_EQUALS("std :: function < void ( int ) > f ;", tokenizeAndStringify(code9, false, true, Settings::Native, "test.cpp", true)); static const char code10[] = "std::tr1::function f;"; ASSERT_EQUALS("std :: tr1 :: function < void ( int ) > f ;", tokenizeAndStringify(code10, false, true, Settings::Native, "test.cpp", false)); ASSERT_EQUALS("std :: function < void ( int ) > f ;", tokenizeAndStringify(code10, false, true, Settings::Native, "test.cpp", true)); // #4042 (Do not add 'std ::' to variables) static const char code11[] = "using namespace std;\n" "const char * string = \"Hi\";"; ASSERT_EQUALS("const char * string ; string = \"Hi\" ;", tokenizeAndStringify(code11, false)); static const char code12[] = "using namespace std;\n" "string f(const char * string) {\n" " cout << string << endl;\n" " return string;\n" "}"; static const char expected12[] = "std :: string f ( const char * string ) {\n" "std :: cout << string << std :: endl ;\n" "return string ;\n" "}"; ASSERT_EQUALS(expected12, tokenizeAndStringify(code12, false)); static const char code13[] = "using namespace std;\n" "try { }\n" "catch(std::exception &exception) { }"; static const char expected13[] = "try { }\n" "catch ( std :: exception & exception ) { }"; ASSERT_EQUALS(expected13, tokenizeAndStringify(code13, false)); // #5773 (Don't prepend 'std ::' to function definitions) static const char code14[] = "using namespace std;\n" "class C {\n" " void search() {}\n" " void search() const {}\n" " void search() THROW_MACRO {}\n" "};"; static const char expected14[] = "class C {\n" "void search ( ) { }\n" "void search ( ) const { }\n" "void search ( ) { }\n" "} ;"; ASSERT_EQUALS(expected14, tokenizeAndStringify(code14, false)); // Ticket #8091 ASSERT_EQUALS("enum Anonymous0 { string } ;", tokenizeAndStringify("using namespace std; " "enum { string };")); ASSERT_EQUALS("enum Type { string } ;", tokenizeAndStringify("using namespace std; " "enum Type { string } ;")); ASSERT_EQUALS("enum class Type { string } ;", tokenizeAndStringify("using namespace std; " "enum class Type { string } ;")); ASSERT_EQUALS("enum struct Type { string } ;", tokenizeAndStringify("using namespace std; " "enum struct Type { string } ;")); ASSERT_EQUALS("enum struct Type : int { f = 0 , string } ;", tokenizeAndStringify("using namespace std; " "enum struct Type : int { f = 0 , string } ;")); ASSERT_EQUALS("enum Type { a , b } ; void foo ( enum Type , std :: string ) { }", tokenizeAndStringify("using namespace std; " "enum Type { a , b } ; void foo ( enum Type , string) {}")); ASSERT_EQUALS("struct T { } ; enum struct Type : int { f = 0 , string } ;", tokenizeAndStringify("using namespace std; " "struct T { typedef int type; } ; " "enum struct Type : T :: type { f = 0 , string } ;")); // Handle garbage enum code "well" ASSERT_EQUALS("enum E : int ; void foo ( ) { std :: string s ; }", tokenizeAndStringify("using namespace std; enum E : int ; void foo ( ) { string s ; }")); } void microsoftMemory() { const char code1a[] = "void foo() { int a[10], b[10]; CopyMemory(a, b, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; int b [ 10 ] ; memcpy ( a , b , sizeof ( a ) ) ; }", tokenizeAndStringify(code1a,false,true,Settings::Win32A)); const char code1b[] = "void foo() { int a[10], b[10]; RtlCopyMemory(a, b, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; int b [ 10 ] ; memcpy ( a , b , sizeof ( a ) ) ; }", tokenizeAndStringify(code1b,false,true,Settings::Win32A)); const char code1c[] = "void foo() { int a[10], b[10]; RtlCopyBytes(a, b, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; int b [ 10 ] ; memcpy ( a , b , sizeof ( a ) ) ; }", tokenizeAndStringify(code1c,false,true,Settings::Win32A)); const char code2a[] = "void foo() { int a[10]; FillMemory(a, sizeof(a), 255); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 255 , sizeof ( a ) ) ; }", tokenizeAndStringify(code2a,false,true,Settings::Win32A)); const char code2b[] = "void foo() { int a[10]; RtlFillMemory(a, sizeof(a), 255); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 255 , sizeof ( a ) ) ; }", tokenizeAndStringify(code2b,false,true,Settings::Win32A)); const char code2c[] = "void foo() { int a[10]; RtlFillBytes(a, sizeof(a), 255); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 255 , sizeof ( a ) ) ; }", tokenizeAndStringify(code2c,false,true,Settings::Win32A)); const char code3a[] = "void foo() { int a[10], b[10]; MoveMemory(a, b, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; int b [ 10 ] ; memmove ( a , b , sizeof ( a ) ) ; }", tokenizeAndStringify(code3a,false,true,Settings::Win32A)); const char code3b[] = "void foo() { int a[10], b[10]; RtlMoveMemory(a, b, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; int b [ 10 ] ; memmove ( a , b , sizeof ( a ) ) ; }", tokenizeAndStringify(code3b,false,true,Settings::Win32A)); const char code4a[] = "void foo() { int a[10]; ZeroMemory(a, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 0 , sizeof ( a ) ) ; }", tokenizeAndStringify(code4a,false,true,Settings::Win32A)); const char code4b[] = "void foo() { int a[10]; RtlZeroMemory(a, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 0 , sizeof ( a ) ) ; }", tokenizeAndStringify(code4b,false,true,Settings::Win32A)); const char code4c[] = "void foo() { int a[10]; RtlZeroBytes(a, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 0 , sizeof ( a ) ) ; }", tokenizeAndStringify(code4c,false,true,Settings::Win32A)); const char code4d[] = "void foo() { int a[10]; RtlSecureZeroMemory(a, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; memset ( a , 0 , sizeof ( a ) ) ; }", tokenizeAndStringify(code4d,false,true,Settings::Win32A)); const char code5[] = "void foo() { int a[10], b[10]; RtlCompareMemory(a, b, sizeof(a)); }"; ASSERT_EQUALS("void foo ( ) { int a [ 10 ] ; int b [ 10 ] ; memcmp ( a , b , sizeof ( a ) ) ; }", tokenizeAndStringify(code5,false,true,Settings::Win32A)); const char code6[] = "void foo() { ZeroMemory(f(1, g(a, b)), h(i, j(0, 1))); }"; ASSERT_EQUALS("void foo ( ) { memset ( f ( 1 , g ( a , b ) ) , 0 , h ( i , j ( 0 , 1 ) ) ) ; }", tokenizeAndStringify(code6,false,true,Settings::Win32A)); const char code7[] = "void foo() { FillMemory(f(1, g(a, b)), h(i, j(0, 1)), 255); }"; ASSERT_EQUALS("void foo ( ) { memset ( f ( 1 , g ( a , b ) ) , 255 , h ( i , j ( 0 , 1 ) ) ) ; }", tokenizeAndStringify(code7,false,true,Settings::Win32A)); } void borland() { // __closure ASSERT_EQUALS("int * a ;", tokenizeAndStringify("int (__closure *a)();", false, true, Settings::Win32A)); // __property ASSERT_EQUALS("class Fred { ; __property ; } ;", tokenizeAndStringify("class Fred { __property int x = { } };", false, true, Settings::Win32A)); } void Qt() { const char code1[] = "class Counter : public QObject " "{ " " Q_OBJECT " "public: " " Counter() { m_value = 0; } " " int value() const { return m_value; } " "public slots: " " void setValue(int value); " "signals: " " void valueChanged(int newValue); " "private: " " int m_value; " "}; " "void Counter::setValue(int value) " "{ " " if (value != m_value) { " " m_value = value; " " emit valueChanged(value); " " } " "}"; const char result1 [] = "class Counter : public QObject " "{ " "public: " "Counter ( ) { m_value = 0 ; } " "int value ( ) const { return m_value ; } " "public: " "void setValue ( int value ) ; " "protected: " "void valueChanged ( int newValue ) ; " "private: " "int m_value ; " "} ; " "void Counter :: setValue ( int value ) " "{ " "if ( value != m_value ) { " "m_value = value ; " "valueChanged ( value ) ; " "} " "}"; ASSERT_EQUALS(result1, tokenizeAndStringify(code1,false)); const char code2[] = "class Counter : public QObject " "{ " " Q_OBJECT " "public: " " Counter() { m_value = 0; } " " int value() const { return m_value; } " "public Q_SLOTS: " " void setValue(int value); " "Q_SIGNALS: " " void valueChanged(int newValue); " "private: " " int m_value; " "};" "void Counter::setValue(int value) " "{ " " if (value != m_value) { " " m_value = value; " " emit valueChanged(value); " " } " "}"; const char result2 [] = "class Counter : public QObject " "{ " "public: " "Counter ( ) { m_value = 0 ; } " "int value ( ) const { return m_value ; } " "public: " "void setValue ( int value ) ; " "protected: " "void valueChanged ( int newValue ) ; " "private: " "int m_value ; " "} ; " "void Counter :: setValue ( int value ) " "{ " "if ( value != m_value ) { " "m_value = value ; " "valueChanged ( value ) ; " "} " "}"; ASSERT_EQUALS(result2, tokenizeAndStringify(code2,false)); const char code3[] = "class MyObject : public QObject {" " MyObject() {}" " ~MyObject() {}" " public slots:" " signals:" " void test() {}" "};"; const char result3 [] = "class MyObject : public QObject { " "MyObject ( ) { } " "~ MyObject ( ) { } " "public: " "protected: " "void test ( ) { } " "} ;"; ASSERT_EQUALS(result3, tokenizeAndStringify(code3,false)); ASSERT_EQUALS("", errout.str()); const char code4[] = "class MyObject : public QObject {" " Q_OBJECT " "public slots:" "};"; const char result4[] = "class MyObject : public QObject { " "public: " "} ;"; ASSERT_EQUALS(result4, tokenizeAndStringify(code4,false)); } void simplifySQL() { // Oracle PRO*C extensions for inline SQL. Just replace the SQL with "asm()" to fix wrong error messages // ticket: #1959 ASSERT_EQUALS("asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL SELECT A FROM B\"\" ) ;", tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL SELECT A FROM B;",false)); ASSERT_THROW(tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL",false), InternalError); ASSERT_EQUALS("asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL EXECUTE BEGIN Proc1 ( A ) ; END ; END - __CPPCHECK_EMBEDDED_SQL_EXEC__\"\" ) ; asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL COMMIT\"\" ) ;", tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL EXECUTE BEGIN Proc1(A); END; END-__CPPCHECK_EMBEDDED_SQL_EXEC__; __CPPCHECK_EMBEDDED_SQL_EXEC__ SQL COMMIT;",false)); ASSERT_EQUALS("asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL UPDATE A SET B = C\"\" ) ; asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL COMMIT\"\" ) ;", tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL UPDATE A SET B = C; __CPPCHECK_EMBEDDED_SQL_EXEC__ SQL COMMIT;",false)); ASSERT_EQUALS("asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL COMMIT\"\" ) ; asm ( \"\"__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL EXECUTE BEGIN Proc1 ( A ) ; END ; END - __CPPCHECK_EMBEDDED_SQL_EXEC__\"\" ) ;", tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL COMMIT; __CPPCHECK_EMBEDDED_SQL_EXEC__ SQL EXECUTE BEGIN Proc1(A); END; END-__CPPCHECK_EMBEDDED_SQL_EXEC__;",false)); ASSERT_THROW(tokenizeAndStringify("int f(){ __CPPCHECK_EMBEDDED_SQL_EXEC__ SQL } int a;",false), InternalError); ASSERT_THROW(tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL int f(){",false), InternalError); ASSERT_THROW(tokenizeAndStringify("__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL END-__CPPCHECK_EMBEDDED_SQL_EXEC__ int a;",false), InternalError); } void simplifyCAlternativeTokens() { ASSERT_EQUALS("void f ( ) { if ( a && b ) { ; } }", tokenizeAndStringify("void f() { if (a and b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( a && b ) { ; } }", tokenizeAndStringify("void f() { if (a and b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( a || b ) { ; } }", tokenizeAndStringify("void f() { if (a or b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( a || b ) { ; } }", tokenizeAndStringify("void f() { if (a or b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( a & b ) { ; } }", tokenizeAndStringify("void f() { if (a bitand b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( a & b ) { ; } }", tokenizeAndStringify("void f() { if (a bitand b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( a | b ) { ; } }", tokenizeAndStringify("void f() { if (a bitor b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( a | b ) { ; } }", tokenizeAndStringify("void f() { if (a bitor b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( a ^ b ) { ; } }", tokenizeAndStringify("void f() { if (a xor b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( a ^ b ) { ; } }", tokenizeAndStringify("void f() { if (a xor b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( ~ b ) { ; } }", tokenizeAndStringify("void f() { if (compl b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( ~ b ) { ; } }", tokenizeAndStringify("void f() { if (compl b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( ! b ) { ; } }", tokenizeAndStringify("void f() { if (not b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( ! b ) { ; } }", tokenizeAndStringify("void f() { if (not b); }", false, true, Settings::Native, "test.cpp")); ASSERT_EQUALS("void f ( ) { if ( a != b ) { ; } }", tokenizeAndStringify("void f() { if (a not_eq b); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( a != b ) { ; } }", tokenizeAndStringify("void f() { if (a not_eq b); }", false, true, Settings::Native, "test.cpp")); // #6201 ASSERT_EQUALS("void f ( ) { if ( ! c || ! memcmp ( a , b , s ) ) { ; } }", tokenizeAndStringify("void f() { if (!c or !memcmp(a, b, s)); }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( ! c || ! memcmp ( a , b , s ) ) { ; } }", tokenizeAndStringify("void f() { if (!c or !memcmp(a, b, s)); }", false, true, Settings::Native, "test.cpp")); // #6029 ASSERT_EQUALS("void f ( ) { if ( ! b ) { } }", tokenizeAndStringify("void f() { if (not b){} }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( ! b ) { } }", tokenizeAndStringify("void f() { if (not b){} }", false, true, Settings::Native, "test.cpp")); // #6207 ASSERT_EQUALS("void f ( ) { if ( not = x ) { } }", tokenizeAndStringify("void f() { if (not=x){} }", false, true, Settings::Native, "test.c")); ASSERT_EQUALS("void f ( ) { if ( not = x ) { } }", tokenizeAndStringify("void f() { if (not=x){} }", false, true, Settings::Native, "test.cpp")); // #8029 ASSERT_EQUALS("void f ( struct S * s ) { x = s . and + 1 ; }", tokenizeAndStringify("void f(struct S *s) { x = s->and + 1; }", false, true, Settings::Native, "test.c")); } void simplifyCalculations() { ASSERT_EQUALS("void foo ( char str [ ] ) { char x ; x = * str ; }", tokenizeAndStringify("void foo ( char str [ ] ) { char x = 0 | ( * str ) ; }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (b + 0) { } }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (0 + b) { } }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (b - 0) { } }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (b * 1) { } }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (1 * b) { } }", true)); //ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", // tokenizeAndStringify("void foo ( ) { if (b / 1) { } }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (b | 0) { } }", true)); ASSERT_EQUALS("void foo ( ) { if ( b ) { } }", tokenizeAndStringify("void foo ( ) { if (0 | b) { } }", true)); ASSERT_EQUALS("void foo ( int b ) { int a ; a = b ; bar ( a ) ; }", tokenizeAndStringify("void foo ( int b ) { int a = b | 0 ; bar ( a ) ; }", true)); ASSERT_EQUALS("void foo ( int b ) { int a ; a = b ; bar ( a ) ; }", tokenizeAndStringify("void foo ( int b ) { int a = 0 | b ; bar ( a ) ; }", true)); // ticket #3093 ASSERT_EQUALS("int f ( ) { return 15 ; }", tokenizeAndStringify("int f() { int a = 10; int b = 5; return a + b; }", true)); ASSERT_EQUALS("int f ( ) { return a ; }", tokenizeAndStringify("int f() { return a * 1; }", true)); ASSERT_EQUALS("int f ( int a ) { return 0 ; }", tokenizeAndStringify("int f(int a) { return 0 * a; }", true)); ASSERT_EQUALS("bool f ( int i ) { switch ( i ) { case 15 : ; return true ; } }", tokenizeAndStringify("bool f(int i) { switch (i) { case 10 + 5: return true; } }", true)); // ticket #3576 - False positives in boolean expressions ASSERT_EQUALS("int foo ( ) { return 1 ; }", tokenizeAndStringify("int foo ( ) { int i; int j; i = 1 || j; return i; }", true)); ASSERT_EQUALS("int foo ( ) { return 0 ; }", tokenizeAndStringify("int foo ( ) { int i; int j; i = 0 && j; return i; }", true)); // ticket #3576 - False positives in boolean expressions // ticket #3723 - Simplify condition (0 && a < 123) ASSERT_EQUALS("( 0 ) ;", tokenizeAndStringify("( 0 && a < 123 );", true)); ASSERT_EQUALS("( 0 ) ;", tokenizeAndStringify("( 0 && a[123] );", true)); // ticket #3964 - simplify numeric calculations in tokenization ASSERT_EQUALS("char a [ 10 ] ;", tokenizeAndStringify("char a[9+1];")); // ticket #4931 ASSERT_EQUALS("dostuff ( 1 ) ;", tokenizeAndStringify("dostuff(9&&8);", true)); } void simplifyRoundCurlyParentheses() { ASSERT_EQUALS("; x = 123 ;", tokenizeAndStringify(";x=({123;});")); ASSERT_EQUALS("; x = y ;", tokenizeAndStringify(";x=({y;});")); } void simplifyOperatorName1() { // make sure C code doesn't get changed const char code[] = "void operator () {}" "int main()" "{" " operator();" "}"; const char result [] = "void operator ( ) { } " "int main ( ) " "{ " "operator ( ) ; " "}"; ASSERT_EQUALS(result, tokenizeAndStringify(code, /*simplify=*/false, /*expand=*/true, /*platform=*/Settings::Native, "test.c")); } void simplifyOperatorName2() { const char code[] = "class Fred" "{" " Fred(const Fred & f) { operator = (f); }" " operator = ();" "}"; const char result [] = "class Fred " "{ " "Fred ( const Fred & f ) { operator= ( f ) ; } " "operator= ( ) ; " "}"; ASSERT_EQUALS(result, tokenizeAndStringify(code,false)); } void simplifyOperatorName3() { // #2615 const char code[] = "void f() {" "static_cast(xResult.operator->())->GetMatrix();" "}"; const char result[] = "void f ( ) { static_cast < ScToken * > ( xResult . operator. ( ) ) . GetMatrix ( ) ; }"; ASSERT_EQUALS(result, tokenizeAndStringify(code,false)); } void simplifyOperatorName4() { const char code[] = "void operator==() { }"; const char result[] = "void operator== ( ) { }"; ASSERT_EQUALS(result, tokenizeAndStringify(code,false)); } void simplifyOperatorName5() { const char code1[] = "std::istream & operator >> (std::istream & s, Fred &f);"; const char result1[] = "std :: istream & operator>> ( std :: istream & s , Fred & f ) ;"; ASSERT_EQUALS(result1, tokenizeAndStringify(code1,false)); const char code2[] = "std::ostream & operator << (std::ostream & s, const Fred &f);"; const char result2[] = "std :: ostream & operator<< ( std :: ostream & s , const Fred & f ) ;"; ASSERT_EQUALS(result2, tokenizeAndStringify(code2,false)); } void simplifyOperatorName6() { // ticket #3195 const char code1[] = "value_type * operator ++ (int);"; const char result1[] = "value_type * operator++ ( int ) ;"; ASSERT_EQUALS(result1, tokenizeAndStringify(code1,false)); const char code2[] = "value_type * operator -- (int);"; const char result2[] = "value_type * operator-- ( int ) ;"; ASSERT_EQUALS(result2, tokenizeAndStringify(code2,false)); } void simplifyOperatorName7() { // ticket #4619 const char code1[] = "value_type * operator += (int);"; const char result1[] = "value_type * operator+= ( int ) ;"; ASSERT_EQUALS(result1, tokenizeAndStringify(code1,false)); } void simplifyOperatorName8() { // ticket #5706 const char code1[] = "value_type * operator += (int) noexcept ;"; const char result1[] = "value_type * operator+= ( int ) noexcept ( true ) ;"; ASSERT_EQUALS(result1, tokenizeAndStringify(code1,false)); const char code2[] = "value_type * operator += (int) noexcept ( true ) ;"; const char result2[] = "value_type * operator+= ( int ) noexcept ( true ) ;"; ASSERT_EQUALS(result2, tokenizeAndStringify(code2,false)); const char code3[] = "value_type * operator += (int) throw ( ) ;"; const char result3[] = "value_type * operator+= ( int ) throw ( ) ;"; ASSERT_EQUALS(result3, tokenizeAndStringify(code3,false)); const char code4[] = "value_type * operator += (int) const noexcept ;"; const char result4[] = "value_type * operator+= ( int ) const noexcept ;"; ASSERT_EQUALS(result4, tokenizeAndStringify(code4,false)); const char code5[] = "value_type * operator += (int) const noexcept ( true ) ;"; const char result5[] = "value_type * operator+= ( int ) const noexcept ( true ) ;"; ASSERT_EQUALS(result5, tokenizeAndStringify(code5,false)); const char code6[] = "value_type * operator += (int) const throw ( ) ;"; const char result6[] = "value_type * operator+= ( int ) const throw ( ) ;"; ASSERT_EQUALS(result6, tokenizeAndStringify(code6,false)); const char code7[] = "value_type * operator += (int) const noexcept ( false ) ;"; const char result7[] = "value_type * operator+= ( int ) const noexcept ( false ) ;"; ASSERT_EQUALS(result7, tokenizeAndStringify(code7,false)); } void simplifyOperatorName9() { // Ticket #5709 const char code[] = "struct R { R operator, ( R b ) ; } ;"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } void simplifyNullArray() { ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;")); } void removeMacrosInGlobalScope() { // remove some unhandled macros in the global scope. ASSERT_EQUALS("void f ( ) { }", tokenizeAndStringify("void f() NOTHROW { }")); ASSERT_EQUALS("struct Foo { } ;", tokenizeAndStringify("struct __declspec(dllexport) Foo {};")); ASSERT_EQUALS("namespace { }", tokenizeAndStringify("ABA() namespace { }")); // #3750 ASSERT_EQUALS("; foo :: foo ( ) { }", tokenizeAndStringify("; AB(foo*) foo::foo() { }")); // #4834 ASSERT_EQUALS("A(B) foo ( ) { }", tokenizeAndStringify("A(B) foo() {}")); // #3855 ASSERT_EQUALS("; class foo { }", tokenizeAndStringify("; AB class foo { }")); ASSERT_EQUALS("; CONST struct ABC abc ;", tokenizeAndStringify("; CONST struct ABC abc ;")); } void removeMacroInVarDecl() { // #4304 // only remove macros with parentheses (those hurt most) ASSERT_EQUALS("void f ( ) { PROGMEM int x ; }", tokenizeAndStringify("void f() { PROGMEM int x ; }")); ASSERT_EQUALS("void f ( ) { int x ; }", tokenizeAndStringify("void f() { SECTION(\".data.ro\") int x ; }")); // various variable declarations ASSERT_EQUALS("void f ( ) { CONST int x ; }", tokenizeAndStringify("void f() { SECTION(\".data.ro\") CONST int x ; }")); ASSERT_EQUALS("void f ( ) { char a [ 4 ] ; }", tokenizeAndStringify("void f() { SECTION(\".data.ro\") char a[4]; }")); ASSERT_EQUALS("void f ( ) { const char a [ 4 ] ; }", tokenizeAndStringify("void f() { SECTION(\".data.ro\") const char a[4]; }")); ASSERT_EQUALS("void f ( ) { struct ABC abc ; }", tokenizeAndStringify("void f() { SECTION(\".data.ro\") struct ABC abc; }")); ASSERT_EQUALS("void f ( ) { CONST struct ABC abc ; }", tokenizeAndStringify("void f() { SECTION(\".data.ro\") CONST struct ABC abc; }")); } void multipleAssignment() { ASSERT_EQUALS("a = b = 0 ;", tokenizeAndStringify("a=b=0;")); } void sizeOfCharLiteral() { // #7490 sizeof('a') should be 4 in C mode std::stringstream expected; expected << "unsigned long a ; a = " << settings1.sizeof_int << " ;"; ASSERT_EQUALS(expected.str(), tokenizeAndStringify("unsigned long a = sizeof('x');", true, true, Settings::Native, "test.c", false)); ASSERT_EQUALS("unsigned long a ; a = 1 ;", tokenizeAndStringify("unsigned long a = sizeof('x');", true, true, Settings::Native, "test.cpp", true)); } void platformWin() { const char code[] = "BOOL f;" "BOOLEAN g;" "BYTE h;" "CHAR i;" "DWORD j;" "FLOAT k;" "INT l;" "INT32 m;" "INT64 n;" "LONG o;" "SHORT p;" "UCHAR q;" "UINT r;" "ULONG s;" "USHORT t;" "WORD u;" "VOID *v;" "LPBOOL w;" "PBOOL x;" "LPBYTE y;" "PBOOLEAN z;" "PBYTE A;" "LPCSTR B;" "PCSTR C;" "LPCVOID D;" "LPDWORD E;" "LPINT F;" "PINT G;" "LPLONG H;" "PLONG I;" "LPSTR J;" "PSTR K;" "PCHAR L;" "LPVOID M;" "PVOID N;" "BOOL _bool;" "HFILE hfile;" "LONG32 long32;" "LCID lcid;" "LCTYPE lctype;" "LGRPID lgrpid;" "LONG64 long64;" "PUCHAR puchar;" "LPCOLORREF lpcolorref;" "PDWORD pdword;" "PULONG pulong;" "SERVICE_STATUS_HANDLE service_status_hanlde;" "SC_LOCK sc_lock;" "SC_HANDLE sc_handle;" "HACCEL haccel;" "HCONV hconv;" "HCONVLIST hconvlist;" "HDDEDATA hddedata;" "HDESK hdesk;" "HDROP hdrop;" "HDWP hdwp;" "HENHMETAFILE henhmetafile;" "HHOOK hhook;" "HKL hkl;" "HMONITOR hmonitor;" "HSZ hsz;" "HWINSTA hwinsta;" "PWCHAR pwchar;" "PUSHORT pushort;" "LANGID langid;" "DWORD64 dword64;" "ULONG64 ulong64;" "LPWSTR lpcwstr;" "LPCWSTR lpcwstr;"; const char expected[] = "int f ; " "unsigned char g ; " "unsigned char h ; " "char i ; " "unsigned long j ; " "float k ; " "int l ; " "int m ; " "long long n ; " "long o ; " "short p ; " "unsigned char q ; " "unsigned int r ; " "unsigned long s ; " "unsigned short t ; " "unsigned short u ; " "void * v ; " "int * w ; " "int * x ; " "unsigned char * y ; " "unsigned char * z ; " "unsigned char * A ; " "const char * B ; " "const char * C ; " "const void * D ; " "unsigned long * E ; " "int * F ; " "int * G ; " "long * H ; " "long * I ; " "char * J ; " "char * K ; " "char * L ; " "void * M ; " "void * N ; " "int _bool ; " "int hfile ; " "int long32 ; " "unsigned long lcid ; " "unsigned long lctype ; " "unsigned long lgrpid ; " "long long long64 ; " "unsigned char * puchar ; " "unsigned long * lpcolorref ; " "unsigned long * pdword ; " "unsigned long * pulong ; " "void * service_status_hanlde ; " "void * sc_lock ; " "void * sc_handle ; " "void * haccel ; " "void * hconv ; " "void * hconvlist ; " "void * hddedata ; " "void * hdesk ; " "void * hdrop ; " "void * hdwp ; " "void * henhmetafile ; " "void * hhook ; " "void * hkl ; " "void * hmonitor ; " "void * hsz ; " "void * hwinsta ; " "wchar_t * pwchar ; " "unsigned short * pushort ; " "unsigned short langid ; " "unsigned long long dword64 ; " "unsigned long long ulong64 ; " "wchar_t * lpcwstr ; " "const wchar_t * lpcwstr ;"; // These types should be defined the same on all Windows platforms std::string win32A = tokenizeAndStringifyWindows(code, true, true, Settings::Win32A); ASSERT_EQUALS(expected, win32A); ASSERT_EQUALS(win32A, tokenizeAndStringifyWindows(code, true, true, Settings::Win32W)); ASSERT_EQUALS(win32A, tokenizeAndStringifyWindows(code, true, true, Settings::Win64)); } void platformWin32() { const char code[] = "unsigned int sizeof_short = sizeof(short);" "unsigned int sizeof_unsigned_short = sizeof(unsigned short);" "unsigned int sizeof_int = sizeof(int);" "unsigned int sizeof_unsigned_int = sizeof(unsigned int);" "unsigned int sizeof_long = sizeof(long);" "unsigned int sizeof_unsigned_long = sizeof(unsigned long);" "unsigned int sizeof_long_long = sizeof(long long);" "unsigned int sizeof_unsigned_long_long = sizeof(unsigned long long);" "unsigned int sizeof_float = sizeof(float);" "unsigned int sizeof_double = sizeof(double);" "unsigned int sizeof_long_double = sizeof(long double);" "unsigned int sizeof_bool = sizeof(bool);" "unsigned int sizeof_wchar_t = sizeof(wchar_t);" "unsigned int sizeof_pointer = sizeof(void *);" "unsigned int sizeof_size_t = sizeof(size_t);" "size_t a;" "ssize_t b;" "ptrdiff_t c;" "intptr_t d;" "uintptr_t e;" "DWORD_PTR O;" "ULONG_PTR P;" "SIZE_T Q;" "HRESULT R;" "LONG_PTR S;" "HANDLE T;" "PHANDLE U;" "SSIZE_T _ssize_t;" "UINT_PTR uint_ptr;" "WPARAM wparam;" "HALF_PTR half_ptr;" "INT_PTR int_ptr;"; const char expected[] = "unsigned int sizeof_short ; sizeof_short = 2 ; " "unsigned int sizeof_unsigned_short ; sizeof_unsigned_short = 2 ; " "unsigned int sizeof_int ; sizeof_int = 4 ; " "unsigned int sizeof_unsigned_int ; sizeof_unsigned_int = 4 ; " "unsigned int sizeof_long ; sizeof_long = 4 ; " "unsigned int sizeof_unsigned_long ; sizeof_unsigned_long = 4 ; " "unsigned int sizeof_long_long ; sizeof_long_long = 8 ; " "unsigned int sizeof_unsigned_long_long ; sizeof_unsigned_long_long = 8 ; " "unsigned int sizeof_float ; sizeof_float = 4 ; " "unsigned int sizeof_double ; sizeof_double = 8 ; " "unsigned int sizeof_long_double ; sizeof_long_double = 8 ; " "unsigned int sizeof_bool ; sizeof_bool = 1 ; " "unsigned int sizeof_wchar_t ; sizeof_wchar_t = 2 ; " "unsigned int sizeof_pointer ; sizeof_pointer = 4 ; " "unsigned int sizeof_size_t ; sizeof_size_t = 4 ; " "unsigned long a ; " "long b ; " "long c ; " "long d ; " "unsigned long e ; " "unsigned long O ; " "unsigned long P ; " "unsigned long Q ; " "long R ; " "long S ; " "void * T ; " "void * * U ; " "long _ssize_t ; " "unsigned int uint_ptr ; " "unsigned int wparam ; " "short half_ptr ; " "int int_ptr ;"; // These types should be defined the same on all Win32 platforms std::string win32A = tokenizeAndStringifyWindows(code, true, true, Settings::Win32A); ASSERT_EQUALS(expected, win32A); ASSERT_EQUALS(win32A, tokenizeAndStringifyWindows(code, true, true, Settings::Win32W)); } void platformWin32A() { const char code[] = "wchar_t wc;" "TCHAR c;" "PTSTR ptstr;" "LPTSTR lptstr;" "PCTSTR pctstr;" "LPCTSTR lpctstr;" "void foo() {" " TCHAR tc = _T(\'c\'); " " TCHAR src[10] = _T(\"123456789\");" " TCHAR dst[10];" " _tcscpy(dst, src);" " dst[0] = 0;" " _tcscat(dst, src);" " LPTSTR d = _tcsdup(str);" " _tprintf(_T(\"Hello world!\"));" " _stprintf(dst, _T(\"Hello!\"));" " _sntprintf(dst, sizeof(dst) / sizeof(TCHAR), _T(\"Hello world!\"));" " _tscanf(_T(\"%s\"), dst);" " _stscanf(dst, _T(\"%s\"), dst);" "}" "TBYTE tbyte;"; const char expected[] = "wchar_t wc ; " "char c ; " "char * ptstr ; " "char * lptstr ; " "const char * pctstr ; " "const char * lpctstr ; " "void foo ( ) { " "char tc ; tc = \'c\' ; " "char src [ 10 ] = \"123456789\" ; " "char dst [ 10 ] ; " "strcpy ( dst , src ) ; " "dst [ 0 ] = 0 ; " "strcat ( dst , src ) ; " "char * d ; d = strdup ( str ) ; " "printf ( \"Hello world!\" ) ; " "sprintf ( dst , \"Hello!\" ) ; " "_snprintf ( dst , sizeof ( dst ) / sizeof ( char ) , \"Hello world!\" ) ; " "scanf ( \"%s\" , dst ) ; " "sscanf ( dst , \"%s\" , dst ) ; " "} " "unsigned char tbyte ;"; ASSERT_EQUALS(expected, tokenizeAndStringifyWindows(code, false, true, Settings::Win32A)); } void platformWin32W() { const char code[] = "wchar_t wc;" "TCHAR c;" "PTSTR ptstr;" "LPTSTR lptstr;" "PCTSTR pctstr;" "LPCTSTR lpctstr;" "TBYTE tbyte;" "void foo() {" " TCHAR tc = _T(\'c\');" " TCHAR src[10] = _T(\"123456789\");" " TCHAR dst[10];" " _tcscpy(dst, src);" " dst[0] = 0;" " _tcscat(dst, src);" " LPTSTR d = _tcsdup(str);" " _tprintf(_T(\"Hello world!\"));" " _stprintf(dst, _T(\"Hello!\"));" " _sntprintf(dst, sizeof(dst) / sizeof(TCHAR), _T(\"Hello world!\"));" " _tscanf(_T(\"%s\"), dst);" " _stscanf(dst, _T(\"%s\"), dst);" "}"; const char expected[] = "wchar_t wc ; " "wchar_t c ; " "wchar_t * ptstr ; " "wchar_t * lptstr ; " "const wchar_t * pctstr ; " "const wchar_t * lpctstr ; " "unsigned wchar_t tbyte ; " "void foo ( ) { " "wchar_t tc ; tc = L\'c\' ; " "wchar_t src [ 10 ] = L\"123456789\" ; " "wchar_t dst [ 10 ] ; " "wcscpy ( dst , src ) ; " "dst [ 0 ] = 0 ; " "wcscat ( dst , src ) ; " "wchar_t * d ; d = wcsdup ( str ) ; " "wprintf ( L\"Hello world!\" ) ; " "swprintf ( dst , L\"Hello!\" ) ; " "_snwprintf ( dst , sizeof ( dst ) / sizeof ( wchar_t ) , L\"Hello world!\" ) ; " "wscanf ( L\"%s\" , dst ) ; " "swscanf ( dst , L\"%s\" , dst ) ; " "}"; ASSERT_EQUALS(expected, tokenizeAndStringifyWindows(code, false, true, Settings::Win32W)); } void platformWin64() { const char code[] = "unsigned int sizeof_short = sizeof(short);" "unsigned int sizeof_unsigned_short = sizeof(unsigned short);" "unsigned int sizeof_int = sizeof(int);" "unsigned int sizeof_unsigned_int = sizeof(unsigned int);" "unsigned int sizeof_long = sizeof(long);" "unsigned int sizeof_unsigned_long = sizeof(unsigned long);" "unsigned int sizeof_long_long = sizeof(long long);" "unsigned int sizeof_unsigned_long_long = sizeof(unsigned long long);" "unsigned int sizeof_float = sizeof(float);" "unsigned int sizeof_double = sizeof(double);" "unsigned int sizeof_long_double = sizeof(long double);" "unsigned int sizeof_bool = sizeof(bool);" "unsigned int sizeof_wchar_t = sizeof(wchar_t);" "unsigned int sizeof_pointer = sizeof(void *);" "unsigned int sizeof_size_t = sizeof(size_t);" "size_t a;" "ssize_t b;" "ptrdiff_t c;" "intptr_t d;" "uintptr_t e;" "DWORD_PTR O;" "ULONG_PTR P;" "SIZE_T Q;" "HRESULT R;" "LONG_PTR S;" "HANDLE T;" "PHANDLE U;" "SSIZE_T _ssize_t;" "UINT_PTR uint_ptr;" "WPARAM wparam;" "HALF_PTR half_ptr;" "INT_PTR int_ptr;"; const char expected[] = "unsigned int sizeof_short ; sizeof_short = 2 ; " "unsigned int sizeof_unsigned_short ; sizeof_unsigned_short = 2 ; " "unsigned int sizeof_int ; sizeof_int = 4 ; " "unsigned int sizeof_unsigned_int ; sizeof_unsigned_int = 4 ; " "unsigned int sizeof_long ; sizeof_long = 4 ; " "unsigned int sizeof_unsigned_long ; sizeof_unsigned_long = 4 ; " "unsigned int sizeof_long_long ; sizeof_long_long = 8 ; " "unsigned int sizeof_unsigned_long_long ; sizeof_unsigned_long_long = 8 ; " "unsigned int sizeof_float ; sizeof_float = 4 ; " "unsigned int sizeof_double ; sizeof_double = 8 ; " "unsigned int sizeof_long_double ; sizeof_long_double = 8 ; " "unsigned int sizeof_bool ; sizeof_bool = 1 ; " "unsigned int sizeof_wchar_t ; sizeof_wchar_t = 2 ; " "unsigned int sizeof_pointer ; sizeof_pointer = 8 ; " "unsigned int sizeof_size_t ; sizeof_size_t = 8 ; " "unsigned long long a ; " "long long b ; " "long long c ; " "long long d ; " "unsigned long long e ; " "unsigned long long O ; " "unsigned long long P ; " "unsigned long long Q ; " "long R ; " "long long S ; " "void * T ; " "void * * U ; " "long long _ssize_t ; " "unsigned long long uint_ptr ; " "unsigned long long wparam ; " "int half_ptr ; " "long long int_ptr ;"; ASSERT_EQUALS(expected, tokenizeAndStringifyWindows(code, true, true, Settings::Win64)); } void platformUnix32() { const char code[] = "unsigned int sizeof_short = sizeof(short);" "unsigned int sizeof_unsigned_short = sizeof(unsigned short);" "unsigned int sizeof_int = sizeof(int);" "unsigned int sizeof_unsigned_int = sizeof(unsigned int);" "unsigned int sizeof_long = sizeof(long);" "unsigned int sizeof_unsigned_long = sizeof(unsigned long);" "unsigned int sizeof_long_long = sizeof(long long);" "unsigned int sizeof_unsigned_long_long = sizeof(unsigned long long);" "unsigned int sizeof_float = sizeof(float);" "unsigned int sizeof_double = sizeof(double);" "unsigned int sizeof_long_double = sizeof(long double);" "unsigned int sizeof_bool = sizeof(bool);" "unsigned int sizeof_wchar_t = sizeof(wchar_t);" "unsigned int sizeof_pointer = sizeof(void *);" "unsigned int sizeof_size_t = sizeof(size_t);" "size_t a;" "ssize_t b;" "ptrdiff_t c;" "intptr_t d;" "uintptr_t e;"; const char expected[] = "unsigned int sizeof_short ; sizeof_short = 2 ; " "unsigned int sizeof_unsigned_short ; sizeof_unsigned_short = 2 ; " "unsigned int sizeof_int ; sizeof_int = 4 ; " "unsigned int sizeof_unsigned_int ; sizeof_unsigned_int = 4 ; " "unsigned int sizeof_long ; sizeof_long = 4 ; " "unsigned int sizeof_unsigned_long ; sizeof_unsigned_long = 4 ; " "unsigned int sizeof_long_long ; sizeof_long_long = 8 ; " "unsigned int sizeof_unsigned_long_long ; sizeof_unsigned_long_long = 8 ; " "unsigned int sizeof_float ; sizeof_float = 4 ; " "unsigned int sizeof_double ; sizeof_double = 8 ; " "unsigned int sizeof_long_double ; sizeof_long_double = 12 ; " "unsigned int sizeof_bool ; sizeof_bool = 1 ; " "unsigned int sizeof_wchar_t ; sizeof_wchar_t = 4 ; " "unsigned int sizeof_pointer ; sizeof_pointer = 4 ; " "unsigned int sizeof_size_t ; sizeof_size_t = 4 ; " "unsigned long a ; " "long b ; " "long c ; " "long d ; " "unsigned long e ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Unix32)); } void platformUnix64() { const char code[] = "unsigned int sizeof_short = sizeof(short);" "unsigned int sizeof_unsigned_short = sizeof(unsigned short);" "unsigned int sizeof_int = sizeof(int);" "unsigned int sizeof_unsigned_int = sizeof(unsigned int);" "unsigned int sizeof_long = sizeof(long);" "unsigned int sizeof_unsigned_long = sizeof(unsigned long);" "unsigned int sizeof_long_long = sizeof(long long);" "unsigned int sizeof_unsigned_long_long = sizeof(unsigned long long);" "unsigned int sizeof_float = sizeof(float);" "unsigned int sizeof_double = sizeof(double);" "unsigned int sizeof_long_double = sizeof(long double);" "unsigned int sizeof_bool = sizeof(bool);" "unsigned int sizeof_wchar_t = sizeof(wchar_t);" "unsigned int sizeof_pointer = sizeof(void *);" "unsigned int sizeof_size_t = sizeof(size_t);" "size_t a;" "ssize_t b;" "ptrdiff_t c;" "intptr_t d;" "uintptr_t e;"; const char expected[] = "unsigned int sizeof_short ; sizeof_short = 2 ; " "unsigned int sizeof_unsigned_short ; sizeof_unsigned_short = 2 ; " "unsigned int sizeof_int ; sizeof_int = 4 ; " "unsigned int sizeof_unsigned_int ; sizeof_unsigned_int = 4 ; " "unsigned int sizeof_long ; sizeof_long = 8 ; " "unsigned int sizeof_unsigned_long ; sizeof_unsigned_long = 8 ; " "unsigned int sizeof_long_long ; sizeof_long_long = 8 ; " "unsigned int sizeof_unsigned_long_long ; sizeof_unsigned_long_long = 8 ; " "unsigned int sizeof_float ; sizeof_float = 4 ; " "unsigned int sizeof_double ; sizeof_double = 8 ; " "unsigned int sizeof_long_double ; sizeof_long_double = 16 ; " "unsigned int sizeof_bool ; sizeof_bool = 1 ; " "unsigned int sizeof_wchar_t ; sizeof_wchar_t = 4 ; " "unsigned int sizeof_pointer ; sizeof_pointer = 8 ; " "unsigned int sizeof_size_t ; sizeof_size_t = 8 ; " "unsigned long a ; " "long b ; " "long c ; " "long d ; " "unsigned long e ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Unix64)); } void platformWin32AStringCat() { //#5150 const char code[] = "TCHAR text[] = _T(\"123\") _T(\"456\") _T(\"789\");"; const char expected[] = "char text [ 10 ] = \"123456789\" ;"; ASSERT_EQUALS(expected, tokenizeAndStringifyWindows(code, true, true, Settings::Win32A)); } void platformWin32WStringCat() { //#5150 const char code[] = "TCHAR text[] = _T(\"123\") _T(\"456\") _T(\"789\");"; const char expected[] = "wchar_t text [ 10 ] = L\"123456789\" ;"; ASSERT_EQUALS(expected, tokenizeAndStringifyWindows(code, true, true, Settings::Win32W)); } void platformWinWithNamespace() { const char code1[] = "UINT32 a; ::UINT32 b; foo::UINT32 c;"; const char expected1[] = "unsigned int a ; unsigned int b ; foo :: UINT32 c ;"; ASSERT_EQUALS(expected1, tokenizeAndStringifyWindows(code1, true, true, Settings::Win32A)); const char code2[] = "LPCVOID a; ::LPCVOID b; foo::LPCVOID c;"; const char expected2[] = "const void * a ; const void * b ; foo :: LPCVOID c ;"; ASSERT_EQUALS(expected2, tokenizeAndStringifyWindows(code2, true, true, Settings::Win32A)); } void isZeroNumber() const { ASSERT_EQUALS(true, Tokenizer::isZeroNumber("0.0")); ASSERT_EQUALS(true, Tokenizer::isZeroNumber("+0.0")); ASSERT_EQUALS(true, Tokenizer::isZeroNumber("-0.0")); ASSERT_EQUALS(true, Tokenizer::isZeroNumber("+0L")); ASSERT_EQUALS(true, Tokenizer::isZeroNumber("+0")); ASSERT_EQUALS(true, Tokenizer::isZeroNumber("-0")); ASSERT_EQUALS(true, Tokenizer::isZeroNumber("-0E+0")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("1.0")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("+1.0")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("-1")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("garbage")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("E2")); ASSERT_EQUALS(false, Tokenizer::isZeroNumber("2e")); } void isOneNumber() const { ASSERT_EQUALS(true, Tokenizer::isOneNumber("1.0")); ASSERT_EQUALS(true, Tokenizer::isOneNumber("+1.0")); ASSERT_EQUALS(true, Tokenizer::isOneNumber("1.0e+0")); ASSERT_EQUALS(true, Tokenizer::isOneNumber("+1L")); ASSERT_EQUALS(true, Tokenizer::isOneNumber("+1")); ASSERT_EQUALS(true, Tokenizer::isOneNumber("1")); ASSERT_EQUALS(true, Tokenizer::isOneNumber("+1E+0")); ASSERT_EQUALS(false, Tokenizer::isOneNumber("0.0")); ASSERT_EQUALS(false, Tokenizer::isOneNumber("+0.0")); ASSERT_EQUALS(false, Tokenizer::isOneNumber("-0")); ASSERT_EQUALS(false, Tokenizer::isOneNumber("")); ASSERT_EQUALS(false, Tokenizer::isOneNumber("garbage")); } void isTwoNumber() const { ASSERT_EQUALS(true, Tokenizer::isTwoNumber("2.0")); ASSERT_EQUALS(true, Tokenizer::isTwoNumber("+2.0")); ASSERT_EQUALS(true, Tokenizer::isTwoNumber("2.0e+0")); ASSERT_EQUALS(true, Tokenizer::isTwoNumber("+2L")); ASSERT_EQUALS(true, Tokenizer::isTwoNumber("+2")); ASSERT_EQUALS(true, Tokenizer::isTwoNumber("2")); ASSERT_EQUALS(true, Tokenizer::isTwoNumber("+2E+0")); ASSERT_EQUALS(false, Tokenizer::isTwoNumber("0.0")); ASSERT_EQUALS(false, Tokenizer::isTwoNumber("+0.0")); ASSERT_EQUALS(false, Tokenizer::isTwoNumber("-0")); ASSERT_EQUALS(false, Tokenizer::isTwoNumber("")); ASSERT_EQUALS(false, Tokenizer::isTwoNumber("garbage")); } void simplifyMathFunctions_erfc() { // verify erfc(), erfcf(), erfcl() - simplifcation const char code_erfc[] ="void f(int x) {\n" " std::cout << erfc(x);\n" // do not simplify " std::cout << erfc(0L);\n" // simplify to 1 "}"; const char expected_erfc[] = "void f ( int x ) {\n" "std :: cout << erfc ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_erfc, tokenizeAndStringify(code_erfc)); const char code_erfcf[] ="void f(float x) {\n" " std::cout << erfcf(x);\n" // do not simplify " std::cout << erfcf(0.0f);\n" // simplify to 1 "}"; const char expected_erfcf[] = "void f ( float x ) {\n" "std :: cout << erfcf ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_erfcf, tokenizeAndStringify(code_erfcf)); const char code_erfcl[] ="void f(long double x) {\n" " std::cout << erfcl(x);\n" // do not simplify " std::cout << erfcl(0.0f);\n" // simplify to 1 "}"; const char expected_erfcl[] = "void f ( long double x ) {\n" "std :: cout << erfcl ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_erfcl, tokenizeAndStringify(code_erfcl)); } void simplifyMathFunctions_cos() { // verify cos(), cosf(), cosl() - simplifcation const char code_cos[] ="void f(int x) {\n" " std::cout << cos(x);\n" // do not simplify " std::cout << cos(0L);\n" // simplify to 1 "}"; const char expected_cos[] = "void f ( int x ) {\n" "std :: cout << cos ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cos, tokenizeAndStringify(code_cos)); const char code_cosf[] ="void f(float x) {\n" " std::cout << cosf(x);\n" // do not simplify " std::cout << cosf(0.0f);\n" // simplify to 1 "}"; const char expected_cosf[] = "void f ( float x ) {\n" "std :: cout << cosf ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cosf, tokenizeAndStringify(code_cosf)); const char code_cosl[] ="void f(long double x) {\n" " std::cout << cosl(x);\n" // do not simplify " std::cout << cosl(0.0f);\n" // simplify to 1 "}"; const char expected_cosl[] = "void f ( long double x ) {\n" "std :: cout << cosl ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cosl, tokenizeAndStringify(code_cosl)); } void simplifyMathFunctions_cosh() { // verify cosh(), coshf(), coshl() - simplifcation const char code_cosh[] ="void f(int x) {\n" " std::cout << cosh(x);\n" // do not simplify " std::cout << cosh(0L);\n" // simplify to 1 "}"; const char expected_cosh[] = "void f ( int x ) {\n" "std :: cout << cosh ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cosh, tokenizeAndStringify(code_cosh)); const char code_coshf[] ="void f(float x) {\n" " std::cout << coshf(x);\n" // do not simplify " std::cout << coshf(0.0f);\n" // simplify to 1 "}"; const char expected_coshf[] = "void f ( float x ) {\n" "std :: cout << coshf ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_coshf, tokenizeAndStringify(code_coshf)); const char code_coshl[] ="void f(long double x) {\n" " std::cout << coshl(x);\n" // do not simplify " std::cout << coshl(0.0f);\n" // simplify to 1 "}"; const char expected_coshl[] = "void f ( long double x ) {\n" "std :: cout << coshl ( x ) ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_coshl, tokenizeAndStringify(code_coshl)); } void simplifyMathFunctions_acos() { // verify acos(), acosf(), acosl() - simplifcation const char code_acos[] ="void f(int x) {\n" " std::cout << acos(x);\n" // do not simplify " std::cout << acos(1L);\n" // simplify to 0 "}"; const char expected_acos[] = "void f ( int x ) {\n" "std :: cout << acos ( x ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_acos, tokenizeAndStringify(code_acos)); const char code_acosf[] ="void f(float x) {\n" " std::cout << acosf(x);\n" // do not simplify " std::cout << acosf(1.0f);\n" // simplify to 0 "}"; const char expected_acosf[] = "void f ( float x ) {\n" "std :: cout << acosf ( x ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_acosf, tokenizeAndStringify(code_acosf)); const char code_acosl[] ="void f(long double x) {\n" " std::cout << acosl(x);\n" // do not simplify " std::cout << acosl(1.0f);\n" // simplify to 0 "}"; const char expected_acosl[] = "void f ( long double x ) {\n" "std :: cout << acosl ( x ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_acosl, tokenizeAndStringify(code_acosl)); } void simplifyMathFunctions_acosh() { // verify acosh(), acoshf(), acoshl() - simplifcation const char code_acosh[] ="void f(int x) {\n" " std::cout << acosh(x);\n" // do not simplify " std::cout << acosh(1L);\n" // simplify to 0 "}"; const char expected_acosh[] = "void f ( int x ) {\n" "std :: cout << acosh ( x ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_acosh, tokenizeAndStringify(code_acosh)); const char code_acoshf[] ="void f(float x) {\n" " std::cout << acoshf(x);\n" // do not simplify " std::cout << acoshf(1.0f);\n" // simplify to 0 "}"; const char expected_acoshf[] = "void f ( float x ) {\n" "std :: cout << acoshf ( x ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_acoshf, tokenizeAndStringify(code_acoshf)); const char code_acoshl[] ="void f(long double x) {\n" " std::cout << acoshl(x);\n" // do not simplify " std::cout << acoshl(1.0f);\n" // simplify to 0 "}"; const char expected_acoshl[] = "void f ( long double x ) {\n" "std :: cout << acoshl ( x ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_acoshl, tokenizeAndStringify(code_acoshl)); } void simplifyMathFunctions_sqrt() { // verify sqrt(), sqrtf(), sqrtl() - simplifcation const char code_sqrt[] ="void f(int x) {\n" " std::cout << sqrt(x);\n" // do not simplify " std::cout << sqrt(-1);\n" // do not simplify " std::cout << sqrt(0L);\n" // simplify to 0 " std::cout << sqrt(1L);\n" // simplify to 1 "}"; const char expected_sqrt[] = "void f ( int x ) {\n" "std :: cout << sqrt ( x ) ;\n" "std :: cout << sqrt ( -1 ) ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_sqrt, tokenizeAndStringify(code_sqrt)); const char code_sqrtf[] ="void f(float x) {\n" " std::cout << sqrtf(x);\n" // do not simplify " std::cout << sqrtf(-1.0f);\n" // do not simplify " std::cout << sqrtf(0.0f);\n" // simplify to 0 " std::cout << sqrtf(1.0);\n" // simplify to 1 "}"; const char expected_sqrtf[] = "void f ( float x ) {\n" "std :: cout << sqrtf ( x ) ;\n" "std :: cout << sqrtf ( -1.0f ) ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_sqrtf, tokenizeAndStringify(code_sqrtf)); const char code_sqrtl[] ="void f(long double x) {\n" " std::cout << sqrtf(x);\n" // do not simplify " std::cout << sqrtf(-1.0);\n" // do not simplify " std::cout << sqrtf(0.0);\n" // simplify to 0 " std::cout << sqrtf(1.0);\n" // simplify to 1 "}"; const char expected_sqrtl[] = "void f ( long double x ) {\n" "std :: cout << sqrtf ( x ) ;\n" "std :: cout << sqrtf ( -1.0 ) ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_sqrtl, tokenizeAndStringify(code_sqrtl)); } void simplifyMathFunctions_cbrt() { // verify cbrt(), cbrtf(), cbrtl() - simplifcation const char code_cbrt[] ="void f(int x) {\n" " std::cout << cbrt(x);\n" // do not simplify " std::cout << cbrt(-1);\n" // do not simplify " std::cout << cbrt(0L);\n" // simplify to 0 " std::cout << cbrt(1L);\n" // simplify to 1 "}"; const char expected_cbrt[] = "void f ( int x ) {\n" "std :: cout << cbrt ( x ) ;\n" "std :: cout << cbrt ( -1 ) ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cbrt, tokenizeAndStringify(code_cbrt)); const char code_cbrtf[] ="void f(float x) {\n" " std::cout << cbrtf(x);\n" // do not simplify " std::cout << cbrtf(-1.0f);\n" // do not simplify " std::cout << cbrtf(0.0f);\n" // simplify to 0 " std::cout << cbrtf(1.0);\n" // simplify to 1 "}"; const char expected_cbrtf[] = "void f ( float x ) {\n" "std :: cout << cbrtf ( x ) ;\n" "std :: cout << cbrtf ( -1.0f ) ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cbrtf, tokenizeAndStringify(code_cbrtf)); const char code_cbrtl[] ="void f(long double x) {\n" " std::cout << cbrtl(x);\n" // do not simplify " std::cout << cbrtl(-1.0);\n" // do not simplify " std::cout << cbrtl(0.0);\n" // simplify to 0 " std::cout << cbrtl(1.0);\n" // simplify to 1 "}"; const char expected_cbrtl[] = "void f ( long double x ) {\n" "std :: cout << cbrtl ( x ) ;\n" "std :: cout << cbrtl ( -1.0 ) ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_cbrtl, tokenizeAndStringify(code_cbrtl)); } void simplifyMathFunctions_exp2() { // verify exp2(), exp2f(), exp2l() - simplifcation const char code_exp2[] ="void f(int x) {\n" " std::cout << exp2(x);\n" // do not simplify " std::cout << exp2(-1);\n" // do not simplify " std::cout << exp2(0L);\n" // simplify to 0 " std::cout << exp2(1L);\n" // do not simplify "}"; const char expected_exp2[] = "void f ( int x ) {\n" "std :: cout << exp2 ( x ) ;\n" "std :: cout << exp2 ( -1 ) ;\n" "std :: cout << 1 ;\n" "std :: cout << exp2 ( 1L ) ;\n" "}"; ASSERT_EQUALS(expected_exp2, tokenizeAndStringify(code_exp2)); const char code_exp2f[] ="void f(float x) {\n" " std::cout << exp2f(x);\n" // do not simplify " std::cout << exp2f(-1.0);\n" // do not simplify " std::cout << exp2f(0.0);\n" // simplify to 1 " std::cout << exp2f(1.0);\n" // do not simplify "}"; const char expected_exp2f[] = "void f ( float x ) {\n" "std :: cout << exp2f ( x ) ;\n" "std :: cout << exp2f ( -1.0 ) ;\n" "std :: cout << 1 ;\n" "std :: cout << exp2f ( 1.0 ) ;\n" "}"; ASSERT_EQUALS(expected_exp2f, tokenizeAndStringify(code_exp2f)); const char code_exp2l[] ="void f(long double x) {\n" " std::cout << exp2l(x);\n" // do not simplify " std::cout << exp2l(-1.0);\n" // do not simplify " std::cout << exp2l(0.0);\n" // simplify to 1 " std::cout << exp2l(1.0);\n" // do not simplify "}"; const char expected_exp2l[] = "void f ( long double x ) {\n" "std :: cout << exp2l ( x ) ;\n" "std :: cout << exp2l ( -1.0 ) ;\n" "std :: cout << 1 ;\n" "std :: cout << exp2l ( 1.0 ) ;\n" "}"; ASSERT_EQUALS(expected_exp2l, tokenizeAndStringify(code_exp2l)); } void simplifyMathFunctions_exp() { // verify exp(), expf(), expl() - simplifcation const char code_exp[] ="void f(int x) {\n" " std::cout << exp(x);\n" // do not simplify " std::cout << exp(-1);\n" // do not simplify " std::cout << exp(0L);\n" // simplify to 1 " std::cout << exp(1L);\n" // do not simplify "}"; const char expected_exp[] = "void f ( int x ) {\n" "std :: cout << exp ( x ) ;\n" "std :: cout << exp ( -1 ) ;\n" "std :: cout << 1 ;\n" "std :: cout << exp ( 1L ) ;\n" "}"; ASSERT_EQUALS(expected_exp, tokenizeAndStringify(code_exp)); const char code_expf[] ="void f(float x) {\n" " std::cout << expf(x);\n" // do not simplify " std::cout << expf(-1.0);\n" // do not simplify " std::cout << expf(0.0);\n" // simplify to 1 " std::cout << expf(1.0);\n" // do not simplify "}"; const char expected_expf[] = "void f ( float x ) {\n" "std :: cout << expf ( x ) ;\n" "std :: cout << expf ( -1.0 ) ;\n" "std :: cout << 1 ;\n" "std :: cout << expf ( 1.0 ) ;\n" "}"; ASSERT_EQUALS(expected_expf, tokenizeAndStringify(code_expf)); const char code_expl[] ="void f(long double x) {\n" " std::cout << expl(x);\n" // do not simplify " std::cout << expl(-1.0);\n" // do not simplify " std::cout << expl(0.0);\n" // simplify to 1 " std::cout << expl(1.0);\n" // do not simplify "}"; const char expected_expl[] = "void f ( long double x ) {\n" "std :: cout << expl ( x ) ;\n" "std :: cout << expl ( -1.0 ) ;\n" "std :: cout << 1 ;\n" "std :: cout << expl ( 1.0 ) ;\n" "}"; ASSERT_EQUALS(expected_expl, tokenizeAndStringify(code_expl)); } void simplifyMathFunctions_erf() { // verify erf(), erff(), erfl() - simplifcation const char code_erf[] ="void f(int x) {\n" " std::cout << erf(x);\n" // do not simplify " std::cout << erf(10);\n" // do not simplify " std::cout << erf(0L);\n" // simplify to 0 "}"; const char expected_erf[] = "void f ( int x ) {\n" "std :: cout << erf ( x ) ;\n" "std :: cout << erf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_erf, tokenizeAndStringify(code_erf)); const char code_erff[] ="void f(float x) {\n" " std::cout << erff(x);\n" // do not simplify " std::cout << erff(10);\n" // do not simplify " std::cout << erff(0.0f);\n" // simplify to 0 "}"; const char expected_erff[] = "void f ( float x ) {\n" "std :: cout << erff ( x ) ;\n" "std :: cout << erff ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_erff, tokenizeAndStringify(code_erff)); const char code_erfl[] ="void f(long double x) {\n" " std::cout << erfl(x);\n" // do not simplify " std::cout << erfl(10.0f);\n" // do not simplify " std::cout << erfl(0.0f);\n" // simplify to 0 "}"; const char expected_erfl[] = "void f ( long double x ) {\n" "std :: cout << erfl ( x ) ;\n" "std :: cout << erfl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_erfl, tokenizeAndStringify(code_erfl)); } void simplifyMathFunctions_atanh() { // verify atanh(), atanhf(), atanhl() - simplifcation const char code_atanh[] ="void f(int x) {\n" " std::cout << atanh(x);\n" // do not simplify " std::cout << atanh(10);\n" // do not simplify " std::cout << atanh(0L);\n" // simplify to 0 "}"; const char expected_atanh[] = "void f ( int x ) {\n" "std :: cout << atanh ( x ) ;\n" "std :: cout << atanh ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_atanh, tokenizeAndStringify(code_atanh)); const char code_atanhf[] ="void f(float x) {\n" " std::cout << atanhf(x);\n" // do not simplify " std::cout << atanhf(10);\n" // do not simplify " std::cout << atanhf(0.0f);\n" // simplify to 0 "}"; const char expected_atanhf[] = "void f ( float x ) {\n" "std :: cout << atanhf ( x ) ;\n" "std :: cout << atanhf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_atanhf, tokenizeAndStringify(code_atanhf)); const char code_atanhl[] ="void f(long double x) {\n" " std::cout << atanhl(x);\n" // do not simplify " std::cout << atanhl(10.0f);\n" // do not simplify " std::cout << atanhl(0.0d);\n" // do not simplify - invalid number! " std::cout << atanhl(0.0f);\n" // simplify to 0 "}"; const char expected_atanhl[] = "void f ( long double x ) {\n" "std :: cout << atanhl ( x ) ;\n" "std :: cout << atanhl ( 10.0f ) ;\n" "std :: cout << atanhl ( 0.0d ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_atanhl, tokenizeAndStringify(code_atanhl)); } void simplifyMathFunctions_atan() { // verify atan(), atanf(), atanl() - simplifcation const char code_atan[] ="void f(int x) {\n" " std::cout << atan(x);\n" // do not simplify " std::cout << atan(10);\n" // do not simplify " std::cout << atan(0L);\n" // simplify to 0 "}"; const char expected_atan[] = "void f ( int x ) {\n" "std :: cout << atan ( x ) ;\n" "std :: cout << atan ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_atan, tokenizeAndStringify(code_atan)); const char code_atanf[] ="void f(float x) {\n" " std::cout << atanf(x);\n" // do not simplify " std::cout << atanf(10);\n" // do not simplify " std::cout << atanf(0.0f);\n" // simplify to 0 "}"; const char expected_atanf[] = "void f ( float x ) {\n" "std :: cout << atanf ( x ) ;\n" "std :: cout << atanf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_atanf, tokenizeAndStringify(code_atanf)); const char code_atanl[] ="void f(long double x) {\n" " std::cout << atanl(x);\n" // do not simplify " std::cout << atanl(10.0f);\n" // do not simplify " std::cout << atanl(0.0f);\n" // simplify to 0 "}"; const char expected_atanl[] = "void f ( long double x ) {\n" "std :: cout << atanl ( x ) ;\n" "std :: cout << atanl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_atanl, tokenizeAndStringify(code_atanl)); } void simplifyMathFunctions_tanh() { // verify tanh(), tanhf(), tanhl() - simplifcation const char code_tanh[] ="void f(int x) {\n" " std::cout << tanh(x);\n" // do not simplify " std::cout << tanh(10);\n" // do not simplify " std::cout << tanh(0L);\n" // simplify to 0 "}"; const char expected_tanh[] = "void f ( int x ) {\n" "std :: cout << tanh ( x ) ;\n" "std :: cout << tanh ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_tanh, tokenizeAndStringify(code_tanh)); const char code_tanhf[] ="void f(float x) {\n" " std::cout << tanhf(x);\n" // do not simplify " std::cout << tanhf(10);\n" // do not simplify " std::cout << tanhf(0.0f);\n" // simplify to 0 "}"; const char expected_tanhf[] = "void f ( float x ) {\n" "std :: cout << tanhf ( x ) ;\n" "std :: cout << tanhf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_tanhf, tokenizeAndStringify(code_tanhf)); const char code_tanhl[] ="void f(long double x) {\n" " std::cout << tanhl(x);\n" // do not simplify " std::cout << tanhl(10.0f);\n" // do not simplify " std::cout << tanhl(0.0f);\n" // simplify to 0 "}"; const char expected_tanhl[] = "void f ( long double x ) {\n" "std :: cout << tanhl ( x ) ;\n" "std :: cout << tanhl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_tanhl, tokenizeAndStringify(code_tanhl)); } void simplifyMathFunctions_tan() { // verify tan(), tanf(), tanl() - simplifcation const char code_tan[] ="void f(int x) {\n" " std::cout << tan(x);\n" // do not simplify " std::cout << tan(10);\n" // do not simplify " std::cout << tan(0L);\n" // simplify to 0 "}"; const char expected_tan[] = "void f ( int x ) {\n" "std :: cout << tan ( x ) ;\n" "std :: cout << tan ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_tan, tokenizeAndStringify(code_tan)); const char code_tanf[] ="void f(float x) {\n" " std::cout << tanf(x);\n" // do not simplify " std::cout << tanf(10);\n" // do not simplify " std::cout << tanf(0.0f);\n" // simplify to 0 "}"; const char expected_tanf[] = "void f ( float x ) {\n" "std :: cout << tanf ( x ) ;\n" "std :: cout << tanf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_tanf, tokenizeAndStringify(code_tanf)); const char code_tanl[] ="void f(long double x) {\n" " std::cout << tanl(x);\n" // do not simplify " std::cout << tanl(10.0f);\n" // do not simplify " std::cout << tanl(0.0f);\n" // simplify to 0 "}"; const char expected_tanl[] = "void f ( long double x ) {\n" "std :: cout << tanl ( x ) ;\n" "std :: cout << tanl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_tanl, tokenizeAndStringify(code_tanl)); } void simplifyMathFunctions_expm1() { // verify expm1(), expm1f(), expm1l() - simplifcation const char code_expm1[] ="void f(int x) {\n" " std::cout << expm1(x);\n" // do not simplify " std::cout << expm1(10);\n" // do not simplify " std::cout << expm1(0L);\n" // simplify to 0 "}"; const char expected_expm1[] = "void f ( int x ) {\n" "std :: cout << expm1 ( x ) ;\n" "std :: cout << expm1 ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_expm1, tokenizeAndStringify(code_expm1)); const char code_expm1f[] ="void f(float x) {\n" " std::cout << expm1f(x);\n" // do not simplify " std::cout << expm1f(10);\n" // do not simplify " std::cout << expm1f(0.0f);\n" // simplify to 0 "}"; const char expected_expm1f[] = "void f ( float x ) {\n" "std :: cout << expm1f ( x ) ;\n" "std :: cout << expm1f ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_expm1f, tokenizeAndStringify(code_expm1f)); const char code_expm1l[] ="void f(long double x) {\n" " std::cout << expm1l(x);\n" // do not simplify " std::cout << expm1l(10.0f);\n" // do not simplify " std::cout << expm1l(0.0f);\n" // simplify to 0 "}"; const char expected_expm1l[] = "void f ( long double x ) {\n" "std :: cout << expm1l ( x ) ;\n" "std :: cout << expm1l ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_expm1l, tokenizeAndStringify(code_expm1l)); } void simplifyMathFunctions_asinh() { // verify asinh(), asinhf(), asinhl() - simplifcation const char code_asinh[] ="void f(int x) {\n" " std::cout << asinh(x);\n" // do not simplify " std::cout << asinh(10);\n" // do not simplify " std::cout << asinh(0L);\n" // simplify to 0 "}"; const char expected_asinh[] = "void f ( int x ) {\n" "std :: cout << asinh ( x ) ;\n" "std :: cout << asinh ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_asinh, tokenizeAndStringify(code_asinh)); const char code_asinhf[] ="void f(float x) {\n" " std::cout << asinhf(x);\n" // do not simplify " std::cout << asinhf(10);\n" // do not simplify " std::cout << asinhf(0.0f);\n" // simplify to 0 "}"; const char expected_asinhf[] = "void f ( float x ) {\n" "std :: cout << asinhf ( x ) ;\n" "std :: cout << asinhf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_asinhf, tokenizeAndStringify(code_asinhf)); const char code_asinhl[] ="void f(long double x) {\n" " std::cout << asinhl(x);\n" // do not simplify " std::cout << asinhl(10.0f);\n" // do not simplify " std::cout << asinhl(0.0f);\n" // simplify to 0 "}"; const char expected_asinhl[] = "void f ( long double x ) {\n" "std :: cout << asinhl ( x ) ;\n" "std :: cout << asinhl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_asinhl, tokenizeAndStringify(code_asinhl)); } void simplifyMathFunctions_asin() { // verify asin(), asinf(), asinl() - simplifcation const char code_asin[] ="void f(int x) {\n" " std::cout << asin(x);\n" // do not simplify " std::cout << asin(10);\n" // do not simplify " std::cout << asin(0L);\n" // simplify to 0 "}"; const char expected_asin[] = "void f ( int x ) {\n" "std :: cout << asin ( x ) ;\n" "std :: cout << asin ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_asin, tokenizeAndStringify(code_asin)); const char code_asinf[] ="void f(float x) {\n" " std::cout << asinf(x);\n" // do not simplify " std::cout << asinf(10);\n" // do not simplify " std::cout << asinf(0.0f);\n" // simplify to 0 "}"; const char expected_asinf[] = "void f ( float x ) {\n" "std :: cout << asinf ( x ) ;\n" "std :: cout << asinf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_asinf, tokenizeAndStringify(code_asinf)); const char code_asinl[] ="void f(long double x) {\n" " std::cout << asinl(x);\n" // do not simplify " std::cout << asinl(10.0f);\n" // do not simplify " std::cout << asinl(0.0f);\n" // simplify to 0 "}"; const char expected_asinl[] = "void f ( long double x ) {\n" "std :: cout << asinl ( x ) ;\n" "std :: cout << asinl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_asinl, tokenizeAndStringify(code_asinl)); } void simplifyMathFunctions_sinh() { // verify sinh(), sinhf(), sinhl() - simplifcation const char code_sinh[] ="void f(int x) {\n" " std::cout << sinh(x);\n" // do not simplify " std::cout << sinh(10);\n" // do not simplify " std::cout << sinh(0L);\n" // simplify to 0 "}"; const char expected_sinh[] = "void f ( int x ) {\n" "std :: cout << sinh ( x ) ;\n" "std :: cout << sinh ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_sinh, tokenizeAndStringify(code_sinh)); const char code_sinhf[] ="void f(float x) {\n" " std::cout << sinhf(x);\n" // do not simplify " std::cout << sinhf(10);\n" // do not simplify " std::cout << sinhf(0.0f);\n" // simplify to 0 "}"; const char expected_sinhf[] = "void f ( float x ) {\n" "std :: cout << sinhf ( x ) ;\n" "std :: cout << sinhf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_sinhf, tokenizeAndStringify(code_sinhf)); const char code_sinhl[] ="void f(long double x) {\n" " std::cout << sinhl(x);\n" // do not simplify " std::cout << sinhl(10.0f);\n" // do not simplify " std::cout << sinhl(0.0f);\n" // simplify to 0 "}"; const char expected_sinhl[] = "void f ( long double x ) {\n" "std :: cout << sinhl ( x ) ;\n" "std :: cout << sinhl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_sinhl, tokenizeAndStringify(code_sinhl)); } void simplifyMathFunctions_sin() { // verify sin(), sinf(), sinl() - simplifcation const char code_sin[] ="void f(int x) {\n" " std::cout << sin(x);\n" // do not simplify " std::cout << sin(10);\n" // do not simplify " std::cout << sin(0L);\n" // simplify to 0 "}"; const char expected_sin[] = "void f ( int x ) {\n" "std :: cout << sin ( x ) ;\n" "std :: cout << sin ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_sin, tokenizeAndStringify(code_sin)); const char code_sinf[] ="void f(float x) {\n" " std::cout << sinf(x);\n" // do not simplify " std::cout << sinf(10);\n" // do not simplify " std::cout << sinf(0.0f);\n" // simplify to 0 "}"; const char expected_sinf[] = "void f ( float x ) {\n" "std :: cout << sinf ( x ) ;\n" "std :: cout << sinf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_sinf, tokenizeAndStringify(code_sinf)); const char code_sinl[] ="void f(long double x) {\n" " std::cout << sinl(x);\n" // do not simplify " std::cout << sinl(10.0f);\n" // do not simplify " std::cout << sinl(0.0f);\n" // simplify to 0 "}"; const char expected_sinl[] = "void f ( long double x ) {\n" "std :: cout << sinl ( x ) ;\n" "std :: cout << sinl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_sinl, tokenizeAndStringify(code_sinl)); // #6629 const char code[] = "class Foo { int sinf; Foo() : sinf(0) {} };"; const char expected[] = "class Foo { int sinf ; Foo ( ) : sinf ( 0 ) { } } ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } void simplifyMathFunctions_ilogb() { // verify ilogb(), ilogbf(), ilogbl() - simplifcation const char code_ilogb[] ="void f(int x) {\n" " std::cout << ilogb(x);\n" // do not simplify " std::cout << ilogb(10);\n" // do not simplify " std::cout << ilogb(1L);\n" // simplify to 0 "}"; const char expected_ilogb[] = "void f ( int x ) {\n" "std :: cout << ilogb ( x ) ;\n" "std :: cout << ilogb ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_ilogb, tokenizeAndStringify(code_ilogb)); const char code_ilogbf[] ="void f(float x) {\n" " std::cout << ilogbf(x);\n" // do not simplify " std::cout << ilogbf(10);\n" // do not simplify " std::cout << ilogbf(1.0f);\n" // simplify to 0 "}"; const char expected_ilogbf[] = "void f ( float x ) {\n" "std :: cout << ilogbf ( x ) ;\n" "std :: cout << ilogbf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_ilogbf, tokenizeAndStringify(code_ilogbf)); const char code_ilogbl[] ="void f(long double x) {\n" " std::cout << ilogbl(x);\n" // do not simplify " std::cout << ilogbl(10.0f);\n" // do not simplify " std::cout << ilogbl(1.0f);\n" // simplify to 0 "}"; const char expected_ilogbl[] = "void f ( long double x ) {\n" "std :: cout << ilogbl ( x ) ;\n" "std :: cout << ilogbl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_ilogbl, tokenizeAndStringify(code_ilogbl)); } void simplifyMathFunctions_logb() { // verify logb(), logbf(), logbl() - simplifcation const char code_logb[] ="void f(int x) {\n" " std::cout << logb(x);\n" // do not simplify " std::cout << logb(10);\n" // do not simplify " std::cout << logb(1L);\n" // simplify to 0 "}"; const char expected_logb[] = "void f ( int x ) {\n" "std :: cout << logb ( x ) ;\n" "std :: cout << logb ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_logb, tokenizeAndStringify(code_logb)); const char code_logbf[] ="void f(float x) {\n" " std::cout << logbf(x);\n" // do not simplify " std::cout << logbf(10);\n" // do not simplify " std::cout << logbf(1.0f);\n" // simplify to 0 "}"; const char expected_logbf[] = "void f ( float x ) {\n" "std :: cout << logbf ( x ) ;\n" "std :: cout << logbf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_logbf, tokenizeAndStringify(code_logbf)); const char code_logbl[] ="void f(long double x) {\n" " std::cout << logbl(x);\n" // do not simplify " std::cout << logbl(10.0f);\n" // do not simplify " std::cout << logbl(1.0f);\n" // simplify to 0 "}"; const char expected_logbl[] = "void f ( long double x ) {\n" "std :: cout << logbl ( x ) ;\n" "std :: cout << logbl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_logbl, tokenizeAndStringify(code_logbl)); } void simplifyMathFunctions_log1p() { // verify log1p(), log1pf(), log1pl() - simplifcation const char code_log1p[] ="void f(int x) {\n" " std::cout << log1p(x);\n" // do not simplify " std::cout << log1p(10);\n" // do not simplify " std::cout << log1p(0L);\n" // simplify to 0 "}"; const char expected_log1p[] = "void f ( int x ) {\n" "std :: cout << log1p ( x ) ;\n" "std :: cout << log1p ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log1p, tokenizeAndStringify(code_log1p)); const char code_log1pf[] ="void f(float x) {\n" " std::cout << log1pf(x);\n" // do not simplify " std::cout << log1pf(10);\n" // do not simplify " std::cout << log1pf(0.0f);\n" // simplify to 0 "}"; const char expected_log1pf[] = "void f ( float x ) {\n" "std :: cout << log1pf ( x ) ;\n" "std :: cout << log1pf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log1pf, tokenizeAndStringify(code_log1pf)); const char code_log1pl[] ="void f(long double x) {\n" " std::cout << log1pl(x);\n" // do not simplify " std::cout << log1pl(10.0f);\n" // do not simplify " std::cout << log1pl(0.0f);\n" // simplify to 0 "}"; const char expected_log1pl[] = "void f ( long double x ) {\n" "std :: cout << log1pl ( x ) ;\n" "std :: cout << log1pl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log1pl, tokenizeAndStringify(code_log1pl)); } void simplifyMathFunctions_log10() { // verify log10(), log10f(), log10l() - simplifcation const char code_log10[] ="void f(int x) {\n" " std::cout << log10(x);\n" // do not simplify " std::cout << log10(10);\n" // do not simplify " std::cout << log10(1L);\n" // simplify to 0 "}"; const char expected_log10[] = "void f ( int x ) {\n" "std :: cout << log10 ( x ) ;\n" "std :: cout << log10 ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log10, tokenizeAndStringify(code_log10)); const char code_log10f[] ="void f(float x) {\n" " std::cout << log10f(x);\n" // do not simplify " std::cout << log10f(10);\n" // do not simplify " std::cout << log10f(1.0f);\n" // simplify to 0 "}"; const char expected_log10f[] = "void f ( float x ) {\n" "std :: cout << log10f ( x ) ;\n" "std :: cout << log10f ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log10f, tokenizeAndStringify(code_log10f)); const char code_log10l[] ="void f(long double x) {\n" " std::cout << log10l(x);\n" // do not simplify " std::cout << log10l(10.0f);\n" // do not simplify " std::cout << log10l(1.0f);\n" // simplify to 0 "}"; const char expected_log10l[] = "void f ( long double x ) {\n" "std :: cout << log10l ( x ) ;\n" "std :: cout << log10l ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log10l, tokenizeAndStringify(code_log10l)); } void simplifyMathFunctions_log() { // verify log(), logf(), logl() - simplifcation const char code_log[] ="void f(int x) {\n" " std::cout << log(x);\n" // do not simplify " std::cout << log(10);\n" // do not simplify " std::cout << log(1L);\n" // simplify to 0 "}"; const char expected_log[] = "void f ( int x ) {\n" "std :: cout << log ( x ) ;\n" "std :: cout << log ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log, tokenizeAndStringify(code_log)); const char code_logf[] ="void f(float x) {\n" " std::cout << logf(x);\n" // do not simplify " std::cout << logf(10);\n" // do not simplify " std::cout << logf(1.0f);\n" // simplify to 0 "}"; const char expected_logf[] = "void f ( float x ) {\n" "std :: cout << logf ( x ) ;\n" "std :: cout << logf ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_logf, tokenizeAndStringify(code_logf)); const char code_logl[] ="void f(long double x) {\n" " std::cout << logl(x);\n" // do not simplify " std::cout << logl(10.0f);\n" // do not simplify " std::cout << logl(1.0f);\n" // simplify to 0 "}"; const char expected_logl[] = "void f ( long double x ) {\n" "std :: cout << logl ( x ) ;\n" "std :: cout << logl ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_logl, tokenizeAndStringify(code_logl)); } void simplifyMathFunctions_log2() { // verify log2(), log2f(), log2l() - simplifcation const char code_log2[] ="void f(int x) {\n" " std::cout << log2(x);\n" // do not simplify " std::cout << log2(10);\n" // do not simplify " std::cout << log2(1L);\n" // simplify to 0 "}"; const char expected_log2[] = "void f ( int x ) {\n" "std :: cout << log2 ( x ) ;\n" "std :: cout << log2 ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log2, tokenizeAndStringify(code_log2)); const char code_log2f[] ="void f(float x) {\n" " std::cout << log2f(x);\n" // do not simplify " std::cout << log2f(10);\n" // do not simplify " std::cout << log2f(1.0f);\n" // simplify to 0 "}"; const char expected_log2f[] = "void f ( float x ) {\n" "std :: cout << log2f ( x ) ;\n" "std :: cout << log2f ( 10 ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log2f, tokenizeAndStringify(code_log2f)); const char code_log2l[] ="void f(long double x) {\n" " std::cout << log2l(x);\n" // do not simplify " std::cout << log2l(10.0f);\n" // do not simplify " std::cout << log2l(1.0f);\n" // simplify to 0 "}"; const char expected_log2l[] = "void f ( long double x ) {\n" "std :: cout << log2l ( x ) ;\n" "std :: cout << log2l ( 10.0f ) ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_log2l, tokenizeAndStringify(code_log2l)); } void simplifyMathFunctions_pow() { // verify pow(),pow(),powl() - simplifcation const char code_pow[] ="void f() {\n" " std::cout << pow(-1.0,1);\n" " std::cout << pow(1.0,1);\n" " std::cout << pow(0,1);\n" " std::cout << pow(1,-6);\n" " std::cout << powf(-1.0,1.0f);\n" " std::cout << powf(1.0,1.0f);\n" " std::cout << powf(0,1.0f);\n" " std::cout << powf(1.0,-6.0f);\n" " std::cout << powl(-1.0,1.0);\n" " std::cout << powl(1.0,1.0);\n" " std::cout << powl(0,1.0);\n" " std::cout << powl(1.0,-6.0d);\n" "}"; const char expected_pow[] = "void f ( ) {\n" "std :: cout << -1.0 ;\n" "std :: cout << 1 ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "std :: cout << -1.0 ;\n" "std :: cout << 1 ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "std :: cout << -1.0 ;\n" "std :: cout << 1 ;\n" "std :: cout << 0 ;\n" "std :: cout << 1 ;\n" "}"; ASSERT_EQUALS(expected_pow, tokenizeAndStringify(code_pow)); // verify if code is simplified correctly. // Do not simplify class members. const char code_pow1[] = "int f(const Fred &fred) {return fred.pow(12,3);}"; const char expected_pow1[] = "int f ( const Fred & fred ) { return fred . pow ( 12 , 3 ) ; }"; ASSERT_EQUALS(expected_pow1, tokenizeAndStringify(code_pow1)); const char code_pow2[] = "int f() {return pow(0,0);}"; const char expected_pow2[] = "int f ( ) { return 1 ; }"; ASSERT_EQUALS(expected_pow2, tokenizeAndStringify(code_pow2)); const char code_pow3[] = "int f() {return pow(0,1);}"; const char expected_pow3[] = "int f ( ) { return 0 ; }"; ASSERT_EQUALS(expected_pow3, tokenizeAndStringify(code_pow3)); const char code_pow4[] = "int f() {return pow(1,0);}"; const char expected_pow4[] = "int f ( ) { return 1 ; }"; ASSERT_EQUALS(expected_pow4, tokenizeAndStringify(code_pow4)); } void simplifyMathFunctions_fmin() { // verify fmin,fminl,fminl simplifcation const char code_fmin[] ="void f() {\n" " std::cout << fmin(-1.0,0);\n" " std::cout << fmin(1.0,0);\n" " std::cout << fmin(0,0);\n" " std::cout << fminf(-1.0,0);\n" " std::cout << fminf(1.0,0);\n" " std::cout << fminf(0,0);\n" " std::cout << fminl(-1.0,0);\n" " std::cout << fminl(1.0,0);\n" " std::cout << fminl(0,0);\n" "}"; const char expected_fmin[] = "void f ( ) {\n" "std :: cout << -1.0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 0 ;\n" "std :: cout << -1.0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 0 ;\n" "std :: cout << -1.0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_fmin, tokenizeAndStringify(code_fmin)); // do not simplify this case const char code_fmin1[] = "float f(float f) { return fmin(f,0);}"; const char expected_fmin1[] = "float f ( float f ) { return fmin ( f , 0 ) ; }"; ASSERT_EQUALS(expected_fmin1, tokenizeAndStringify(code_fmin1)); } void simplifyMathFunctions_fmax() { // verify fmax(),fmax(),fmaxl() simplifcation const char code_fmax[] ="void f() {\n" " std::cout << fmax(-1.0,0);\n" " std::cout << fmax(1.0,0);\n" " std::cout << fmax(0,0);\n" " std::cout << fmaxf(-1.0,0);\n" " std::cout << fmaxf(1.0,0);\n" " std::cout << fmaxf(0,0);\n" " std::cout << fmaxl(-1.0,0);\n" " std::cout << fmaxl(1.0,0);\n" " std::cout << fmaxl(0,0);\n" "}"; const char expected_fmax[] = "void f ( ) {\n" "std :: cout << 0 ;\n" "std :: cout << 1.0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 1.0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 0 ;\n" "std :: cout << 1.0 ;\n" "std :: cout << 0 ;\n" "}"; ASSERT_EQUALS(expected_fmax, tokenizeAndStringify(code_fmax)); // do not simplify this case const char code_fmax1[] = "float f(float f) { return fmax(f,0);}"; const char expected_fmax1[] = "float f ( float f ) { return fmax ( f , 0 ) ; }"; ASSERT_EQUALS(expected_fmax1, tokenizeAndStringify(code_fmax1)); } void simplifyMathExpressions() { //#1620 const char code1[] = "void foo() {\n" " std::cout< ( ) ) : 0 ;", tokenizeAndStringify("typedef std::map mymap; a ? mymap() : 0;")); } std::string testAst(const char code[],bool verbose=false) { // tokenize given code.. Tokenizer tokenList(&settings0, nullptr); std::istringstream istr(code); if (!tokenList.list.createTokens(istr,"test.cpp")) return "ERROR"; tokenList.combineOperators(); tokenList.createLinks(); tokenList.createLinks2(); // Create AST.. tokenList.prepareTernaryOpForAST(); tokenList.list.createAst(); tokenList.list.validateAst(); // Basic AST validation for (const Token *tok = tokenList.list.front(); tok; tok = tok->next()) { if (tok->astOperand2() && !tok->astOperand1() && tok->str() != ";" && tok->str() != ":") return "Op2 but no Op1 for token: " + tok->str(); } // Return stringified AST if (verbose) return tokenList.list.front()->astTop()->astStringVerbose(0, 0); std::string ret; std::set astTop; for (const Token *tok = tokenList.list.front(); tok; tok = tok->next()) { if (tok->astOperand1() && astTop.find(tok->astTop()) == astTop.end()) { astTop.insert(tok->astTop()); if (!ret.empty()) ret = ret + " "; ret += tok->astTop()->astString(); } } return ret; } void astexpr() { // simple expressions with arithmetical ops ASSERT_EQUALS("12+3+", testAst("1+2+3")); ASSERT_EQUALS("12*3+", testAst("1*2+3")); ASSERT_EQUALS("123*+", testAst("1+2*3")); ASSERT_EQUALS("12*34*+", testAst("1*2+3*4")); ASSERT_EQUALS("12*34*5*+", testAst("1*2+3*4*5")); ASSERT_EQUALS("0(r.&", testAst("(&((typeof(x))0).r);")); ASSERT_EQUALS("0(r.&", testAst("&((typeof(x))0).r;")); // Various tests of precedence ASSERT_EQUALS("ab::c+", testAst("a::b+c")); ASSERT_EQUALS("abc+=", testAst("a=b+c")); ASSERT_EQUALS("abc=,", testAst("a,b=c")); ASSERT_EQUALS("a-1+", testAst("-a+1")); ASSERT_EQUALS("ab++-c-", testAst("a-b++-c")); // sizeof ASSERT_EQUALS("ab.sizeof", testAst("sizeof a.b")); // assignment operators ASSERT_EQUALS("ab>>=", testAst("a>>=b;")); ASSERT_EQUALS("ab<<=", testAst("a<<=b;")); ASSERT_EQUALS("ab+=", testAst("a+=b;")); ASSERT_EQUALS("ab-=", testAst("a-=b;")); ASSERT_EQUALS("ab*=", testAst("a*=b;")); ASSERT_EQUALS("ab/=", testAst("a/=b;")); ASSERT_EQUALS("ab%=", testAst("a%=b;")); ASSERT_EQUALS("ab&=", testAst("a&=b;")); ASSERT_EQUALS("ab|=", testAst("a|=b;")); ASSERT_EQUALS("ab^=", testAst("a^=b;")); ASSERT_EQUALS("ab*c*.(+return", testAst("return a + ((*b).*c)();")); // assignments are executed from right to left ASSERT_EQUALS("abc==", testAst("a=b=c;")); // ternary operator ASSERT_EQUALS("ab0=c1=:?", testAst("a?b=0:c=1;")); ASSERT_EQUALS("fabc,d:?=e,", testAst("f = a ? b, c : d, e;")); ASSERT_EQUALS("fabc,de,:?=", testAst("f = (a ? (b, c) : (d, e));")); ASSERT_EQUALS("fabc,de,:?=", testAst("f = (a ? b, c : (d, e));")); ASSERT_EQUALS("ab35,4:?foo(:?return", testAst("return (a ? b ? (3,5) : 4 : foo());")); ASSERT_EQUALS("check(result_type00,{invalid:?return", testAst("return check() ? result_type {0, 0} : invalid;")); ASSERT_EQUALS("a\"\"=", testAst("a=\"\"")); ASSERT_EQUALS("a\'\'=", testAst("a=\'\'")); ASSERT_EQUALS("a1[\"\"=", testAst("char a[1]=\"\";")); ASSERT_EQUALS("'X''a'>", testAst("('X' > 'a')")); ASSERT_EQUALS("'X''a'>", testAst("(L'X' > L'a')")); ASSERT_EQUALS("a0>bc/d:?", testAst("(a>0) ? (b/(c)) : d;")); ASSERT_EQUALS("abc/+d+", testAst("a + (b/(c)) + d;")); ASSERT_EQUALS("f( x1024x/0:?", testAst("void f() { x ? 1024 / x : 0; }")); ASSERT_EQUALS("absizeofd(ef.+(=", testAst("a = b(sizeof(c d) + e.f)")); ASSERT_EQUALS("a*b***", testAst("*a * **b;")); // Correctly distinguish between unary and binary operator* // strings ASSERT_EQUALS("f\"A\"1,(",testAst("f(\"A\" B, 1);")); ASSERT_EQUALS("fA1,(",testAst("f(A \"B\", 1);")); // C++ : type() ASSERT_EQUALS("fint(0,(", testAst("f(int(),0);")); ASSERT_EQUALS("f(0,(", testAst("f(int *(),0);")); // typedef int* X; f(X(),0); ASSERT_EQUALS("f((0,(", testAst("f((intp)int *(),0);")); ASSERT_EQUALS("zx1(&y2(&|=", testAst("z = (x & (unsigned)1) | (y & (unsigned)2);")); // not type() // for ASSERT_EQUALS("for;;(", testAst("for(;;)")); ASSERT_EQUALS("fora0=a8 c : d);")); ASSERT_EQUALS("forde:(", testAst("for (a::b d : e);")); ASSERT_EQUALS("forx*0=yz;;(", testAst("for(*x=0;y;z)")); ASSERT_EQUALS("forx0=y(8();")); ASSERT_EQUALS("X12,3,(new", testAst("new (a,b,c) X(1,2,3);")); ASSERT_EQUALS("aXnew(", testAst("a (new (X));")); ASSERT_EQUALS("aXnew5,(", testAst("a (new (X), 5);")); ASSERT_EQUALS("adelete", testAst("delete a;")); ASSERT_EQUALS("adelete", testAst("delete (a);")); ASSERT_EQUALS("adelete", testAst("delete[] a;")); ASSERT_EQUALS("ab.3c-(delete", testAst("delete[] a.b(3 - c);")); ASSERT_EQUALS("a::new=", testAst("a = new (b) ::X;")); ASSERT_EQUALS("aA1(new(bB2(new(,", testAst("a(new A(1)), b(new B(2))")); ASSERT_EQUALS("Fred10[new", testAst(";new Fred[10];")); ASSERT_EQUALS("f( adelete", testAst("void f() { delete a; }")); // invalid code (libreoffice), don't hang // #define SlideSorterViewShell // SfxViewFrame* pFrame; // new SlideSorterViewShell(pFrame,rViewShellBase,pParentWindow,pFrameViewArgument); ASSERT_EQUALS("fxnewy,z,(", testAst("f(new (x,y,z));")); // clang testsuite.. ASSERT_EQUALS("const0(new", testAst("new const auto (0);")); ASSERT_EQUALS("autonew", testAst("new (auto) (0.0);")); ASSERT_EQUALS("int3[4[5[new", testAst("new (int S::*[3][4][5]) ();")); ASSERT_EQUALS("pSnew=", testAst("p=new (x)(S)(1,2);")); ASSERT_EQUALS("inti[new(", testAst("(void)new (int[i]);")); ASSERT_EQUALS("intp* pnew malloc4(", testAst("int*p; new (p) (malloc(4));")); ASSERT_EQUALS("intnew", testAst("new (&w.x)(int*)(0);")); ASSERT_EQUALS("&new", testAst("new (&w.x)(0);")); // <- the "(int*)" has been simplified // gcc testsuite.. ASSERT_EQUALS("char10[new(", testAst("(void)new(char*)[10];")); } void astpar() { // parentheses ASSERT_EQUALS("12+3*", testAst("(1+2)*3")); ASSERT_EQUALS("123+*", testAst("1*(2+3)")); ASSERT_EQUALS("123+*4*", testAst("1*(2+3)*4")); ASSERT_EQUALS("ifab.c&d==(", testAst("if((a.b&c)==d){}")); ASSERT_EQUALS("pf.pf.12,(&&", testAst("((p.f) && (p.f)(1,2))")); // problems with: if (x[y]==z) ASSERT_EQUALS("ifa(0[1==(", testAst("if(a()[0]==1){}")); ASSERT_EQUALS("ifbuff0[&(*1==(", testAst("if (*((DWORD*)&buff[0])==1){}")); ASSERT_EQUALS("ifp*0[1==(", testAst("if((*p)[0]==1)")); ASSERT_EQUALS("ifab.cd.[e==(", testAst("if(a.b[c.d]==e){}")); ASSERT_EQUALS("iftpnote.i1-[note.0==tpnote.i1-[type.4>||(", testAst("if ((tp.note[i - 1].note == 0) || (tp.note[i - 1].type > 4)) {}")); ASSERT_EQUALS("ab.i[j1+[", testAst("a.b[i][j+1]")); // problems with: x=expr ASSERT_EQUALS("=\n" "|-x\n" "`-(\n" " `-.\n" " |-[\n" " | |-a\n" " | `-i\n" " `-f\n", testAst("x = ((a[i]).f)();", true)); ASSERT_EQUALS("abc.de.++[=", testAst("a = b.c[++(d.e)];")); ASSERT_EQUALS("abc(1+=", testAst("a = b(c**)+1;")); ASSERT_EQUALS("abc.=", testAst("a = (b).c;")); // casts ASSERT_EQUALS("a1(2(+=",testAst("a=(t)1+(t)2;")); ASSERT_EQUALS("a1(2+=",testAst("a=(t)1+2;")); ASSERT_EQUALS("a1(2+=",testAst("a=(t*)1+2;")); ASSERT_EQUALS("a1(2+=",testAst("a=(t&)1+2;")); ASSERT_EQUALS("ab::r&c(=", testAst("a::b& r = (a::b&)c;")); // #5261 ASSERT_EQUALS("ab10:?=", testAst("a=(b)?1:0;")); // TODO: This AST is incomplete however it's very weird syntax (taken from clang test suite) ASSERT_EQUALS("a&(", testAst("(int (**)[i]){&a}[0][1][5] = 0;")); ASSERT_EQUALS("n0=", testAst("TrivialDefCtor{[2][2]}[1][1].n = 0;")); ASSERT_EQUALS("aT12,3,{1[=", testAst("a = T{1, 2, 3}[1];")); // ({..}) ASSERT_EQUALS("a{+d+ bc+", testAst("a+({b+c;})+d")); ASSERT_EQUALS("a{d*+ bc+", testAst("a+({b+c;})*d")); ASSERT_EQUALS("xa{((= bc( yd{((= ef(", testAst("x=(int)(a({b(c);}));" // don't hang "y=(int)(d({e(f);}));")); ASSERT_EQUALS("QT_WA{{,( x0= QT_WA{{,( x1= x2=", testAst("QT_WA({},{x=0;});" // don't hang "QT_WA({x=1;},{x=2;});")); ASSERT_EQUALS("xMACROtype.T=value.1=,{({=", testAst("x = { MACRO( { .type=T, .value=1 } ) }")); // don't hang: MACRO({..}) ASSERT_EQUALS("fori10=i{;;( i--", testAst("for (i=10;i;({i--;}) ) {}")); // function pointer TODO_ASSERT_EQUALS("todo", "va_argapvoid((,(*0=", testAst("*va_arg(ap, void(**) ()) = 0;")); // struct initialization ASSERT_EQUALS("name_bytes[bits~unusedBits>>unusedBits<<{=", testAst("const uint8_t name_bytes[] = { (~bits >> unusedBits) << unusedBits };")); ASSERT_EQUALS("abuf.0{={=", testAst("a = { .buf = { 0 } };")); ASSERT_EQUALS("ab2[a.0=b.0=,{a.0=b.0=,{,{=", testAst("struct AB ab[2] = { { .a=0, .b=0 }, { .a=0, .b=0 } };")); ASSERT_EQUALS("tset{=", testAst("struct cgroup_taskset tset = {};")); ASSERT_EQUALS("s1a&,{2b&,{,{=", testAst("s = { {1, &a}, {2, &b} };")); ASSERT_EQUALS("s0[L.2[x={=", testAst("s = { [0].L[2] = x};")); ASSERT_EQUALS("ac.0={(=", testAst("a = (b){.c=0,};")); // <- useless comma ASSERT_EQUALS("xB[1y.z.1={(&=,{={=", testAst("x = { [B] = {1, .y = &(struct s) { .z=1 } } };")); // struct initialization hang ASSERT_EQUALS("sbar.1{,{(={= fcmd( forfieldfield++;;(", testAst("struct S s = {.bar = (struct foo) { 1, { } } };\n" "void f(struct cmd *) { for (; field; field++) {} }")); // template parentheses: <> ASSERT_EQUALS("stdfabs::m_similarity(numeric_limitsepsilon::(<=return", testAst("return std::fabs(m_similarity) <= numeric_limits::epsilon();")); // #6195 // C++ initializer ASSERT_EQUALS("Class{", testAst("Class{};")); ASSERT_EQUALS("Class12,{", testAst("Class{1,2};")); ASSERT_EQUALS("Class12,{", testAst("Class{1,2};")); ASSERT_EQUALS("abc{d:?=", testAst("a=b?c{}:d;")); ASSERT_EQUALS("abc12,{d:?=", testAst("a=b?c{1,2}:d;")); ASSERT_EQUALS("abc{d:?=", testAst("a=b?c{}:d;")); ASSERT_EQUALS("abc12,{d:?=", testAst("a=b?c{1,2}:d;")); ASSERT_EQUALS("a::12,{", testAst("::a{1,2};")); // operator precedence ASSERT_EQUALS("Abc({newreturn", testAst("return new A {b(c)};")); ASSERT_EQUALS("a{{return", testAst("return{{a}};")); ASSERT_EQUALS("a{b{,{return", testAst("return{{a},{b}};")); } void astbrackets() { // [] ASSERT_EQUALS("a23+[4+", testAst("a[2+3]+4")); ASSERT_EQUALS("a1[0[", testAst("a[1][0]")); ASSERT_EQUALS("ab0[=", testAst("a=(b)[0];")); ASSERT_EQUALS("abc.0[=", testAst("a=b.c[0];")); ASSERT_EQUALS("ab0[1[=", testAst("a=b[0][1];")); } void astunaryop() { // unary operators ASSERT_EQUALS("1a--+", testAst("1 + --a")); ASSERT_EQUALS("1a--+", testAst("1 + a--")); ASSERT_EQUALS("ab+!", testAst("!(a+b)")); ASSERT_EQUALS("ab.++", testAst("++a.b;")); ASSERT_EQUALS("ab.++", testAst("a.b++;")); ASSERT_EQUALS("ab::++", testAst("a::b++;")); ASSERT_EQUALS("c5[--*", testAst("*c[5]--;")); ASSERT_EQUALS("a*bc:?return", testAst("return *a ? b : c;")); // Unary :: operator ASSERT_EQUALS("abcd::12,(e/:?=", testAst("a = b ? c : ::d(1,2) / e;")); // how is "--" handled here: ASSERT_EQUALS("ab4<,etc.. ASSERT_EQUALS("a(3==", testAst("a()==3")); ASSERT_EQUALS("ab(== f(", testAst("a == b(); f();")); ASSERT_EQUALS("static_casta(i[", testAst("; static_cast(a)[i];")); // #6203 ASSERT_EQUALS("reinterpret_castreinterpret_castptr(123&(", testAst(";reinterpret_cast(reinterpret_cast(ptr) & 123);")); // #7253 ASSERT_EQUALS("bcd.(=", testAst(";a && b = c->d();")); // This two unit tests were added to avoid a crash. The actual correct AST result for non-executable code has not been determined so far. ASSERT_EQUALS("Cpublica::b:::", testAst("class C : public ::a::b { };")); ASSERT_EQUALS("AB: f( abc+=", testAst("struct A : public B { void f() { a=b+c; } };")); } void astcast() { ASSERT_EQUALS("ac&(=", testAst("a = (long)&c;")); ASSERT_EQUALS("ac*(=", testAst("a = (Foo*)*c;")); ASSERT_EQUALS("ac-(=", testAst("a = (long)-c;")); ASSERT_EQUALS("ac~(=", testAst("a = (b)~c;")); ASSERT_EQUALS("ac(=", testAst("a = (some)c;")); ASSERT_EQUALS("afoveon_avgimage((foveon_avgimage((+=", testAst("a = foveon_avg(((short(*)[4]) image)) + foveon_avg(((short(*)[4]) image));")); ASSERT_EQUALS("c(40< int { return 0; }();")); ASSERT_EQUALS("{([(return 0return", testAst("return [something]() -> int { return 0; }();")); ASSERT_EQUALS("{([cd,(return 0return", testAst("return [](int a, int b) -> int { return 0; }(c, d);")); TODO_ASSERT_EQUALS("x{([=", "stdconst::x{([=&", testAst("x = [&]()->std::string const & { 1; }")); ASSERT_EQUALS("x{([= 0return", testAst("x = [](){return 0; };")); ASSERT_EQUALS("ab{[(= cd=", testAst("a = b([&]{c=d;});")); } void astcase() { ASSERT_EQUALS("0case", testAst("case 0:")); ASSERT_EQUALS("12+case", testAst("case 1+2:")); ASSERT_EQUALS("xyz:?case", testAst("case (x?y:z):")); } void compileLimits() { const char raw_code[] = "#define PTR1 (* (* (* (* (* (* (* (* (* (*\n" "#define PTR2 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1\n" "#define PTR3 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2\n" "#define PTR4 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3\n" "#define PTR5 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4\n" "#define PTR6 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5\n" "\n" "#define RBR1 ) ) ) ) ) ) ) ) ) )\n" "#define RBR2 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1\n" "#define RBR3 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2\n" "#define RBR4 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3\n" "#define RBR5 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4\n" "#define RBR6 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5\n" "\n" "int PTR4 q4_var RBR4 = 0;\n"; // Preprocess file.. Preprocessor preprocessor(settings0); std::list configurations; std::string filedata; std::istringstream fin(raw_code); preprocessor.preprocess(fin, filedata, configurations, emptyString, settings0.includePaths); const std::string code = preprocessor.getcode(filedata, emptyString, emptyString); tokenizeAndStringify(code.c_str()); // just survive... } bool isStartOfExecutableScope(int offset, const char code[]) { Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); return Tokenizer::startOfExecutableScope(tokenizer.tokens()->tokAt(offset)) != nullptr; } void startOfExecutableScope() { ASSERT(isStartOfExecutableScope(3, "void foo() { }")); ASSERT(isStartOfExecutableScope(3, "void foo() const { }")); ASSERT(isStartOfExecutableScope(3, "void foo() volatile { }")); ASSERT(isStartOfExecutableScope(3, "void foo() override { }")); ASSERT(isStartOfExecutableScope(3, "void foo() noexcept { }")); ASSERT(isStartOfExecutableScope(3, "void foo() NOEXCEPT { }")); ASSERT(isStartOfExecutableScope(3, "void foo() CONST NOEXCEPT { }")); ASSERT(isStartOfExecutableScope(3, "void foo() const noexcept { }")); ASSERT(isStartOfExecutableScope(3, "void foo() noexcept(true) { }")); ASSERT(isStartOfExecutableScope(3, "void foo() const noexcept(true) { }")); ASSERT(isStartOfExecutableScope(3, "void foo() throw() { }")); ASSERT(isStartOfExecutableScope(3, "void foo() THROW() { }")); ASSERT(isStartOfExecutableScope(3, "void foo() CONST THROW() { }")); ASSERT(isStartOfExecutableScope(3, "void foo() const throw() { }")); ASSERT(isStartOfExecutableScope(3, "void foo() throw(int) { }")); ASSERT(isStartOfExecutableScope(3, "void foo() const throw(int) { }")); ASSERT(isStartOfExecutableScope(2, "foo() : a(1) { }")); ASSERT(isStartOfExecutableScope(2, "foo() : a(1), b(2) { }")); ASSERT(isStartOfExecutableScope(2, "foo() : a{1} { }")); ASSERT(isStartOfExecutableScope(2, "foo() : a{1}, b{2} { }")); } void removeMacroInClassDef() { // #6058 ASSERT_EQUALS("class Fred { } ;", tokenizeAndStringify("class DLLEXPORT Fred { } ;")); ASSERT_EQUALS("class Fred : Base { } ;", tokenizeAndStringify("class Fred FINAL : Base { } ;")); // Regression for C code: ASSERT_EQUALS("struct Fred { } ;", tokenizeAndStringify("struct DLLEXPORT Fred { } ;", false, true, Settings::Native, "test.c")); } void sizeofAddParentheses() { ASSERT_EQUALS("sizeof ( sizeof ( 1 ) ) ;", tokenizeAndStringify("sizeof sizeof 1;")); ASSERT_EQUALS("sizeof ( a . b ) + 3 ;", tokenizeAndStringify("sizeof a.b+3;")); ASSERT_EQUALS("sizeof ( a [ 2 ] . b ) + 3 ;", tokenizeAndStringify("sizeof a[2].b+3;")); ASSERT_EQUALS("f ( 0 , sizeof ( ptr . bar ) ) ;", tokenizeAndStringify("f(0, sizeof ptr->bar );")); } void findGarbageCode() { // Make sure the Tokenizer::findGarbageCode() does not have FPs // before if|for|while|switch ASSERT_NO_THROW(tokenizeAndStringify("void f() { do switch (a) {} while (1); }")) ASSERT_NO_THROW(tokenizeAndStringify("void f() { label: switch (a) {} }")); ASSERT_NO_THROW(tokenizeAndStringify("void f() { UNKNOWN_MACRO if (a) {} }")) // TODO ASSERT_NO_THROW(tokenizeAndStringify("void f() { MACRO(switch); }")); // TODO ASSERT_NO_THROW(tokenizeAndStringify("void f() { MACRO(x,switch); }")); // after (expr) ASSERT_NO_THROW(tokenizeAndStringify("void f() { switch (a) int b; }")); } void checkConfig(const char code[]) { errout.str(""); Settings s; s.checkConfiguration = true; // tokenize.. Tokenizer tokenizer(&s, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); } void checkConfiguration() { checkConfig("void f() { DEBUG(x();y()); }"); ASSERT_EQUALS("[test.cpp:1]: (information) Ensure that 'DEBUG' is defined either using -I, --include or -D.\n", errout.str()); } }; REGISTER_TEST(TestTokenizer) cppcheck-1.82/test/testtokenlist.cpp000066400000000000000000000045431322667425100176410ustar00rootroot00000000000000/* * 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 "settings.h" #include "testsuite.h" #include "token.h" #include "tokenlist.h" #include #include class TestTokenList : public TestFixture { public: TestTokenList() : TestFixture("TestTokenList") { } private: Settings settings; void run() { TEST_CASE(testaddtoken1); TEST_CASE(testaddtoken2); TEST_CASE(inc); } // inspired by #5895 void testaddtoken1() { const std::string code = "0x89504e470d0a1a0a"; TokenList tokenlist(&settings); tokenlist.addtoken(code, 1, 1, false); ASSERT_EQUALS("9894494448401390090U", tokenlist.front()->str()); // that is supposed to break on 32bit //unsigned long numberUL(0); //std::istringstream(tokenlist.front()->str()) >> numberUL; //ASSERT_EQUALS(9894494448401390090U, numberUL); unsigned long long numberULL(0); std::istringstream(tokenlist.front()->str()) >> numberULL; ASSERT_EQUALS(9894494448401390090U, numberULL); } void testaddtoken2() { const std::string code = "0xF0000000"; settings.int_bit = 32; TokenList tokenlist(&settings); tokenlist.addtoken(code, 1, 1, false); ASSERT_EQUALS("4026531840U", tokenlist.front()->str()); } void inc() const { const char code[] = "a++1;1++b;"; errout.str(""); // tokenize.. TokenList tokenlist(&settings); std::istringstream istr(code); tokenlist.createTokens(istr, "a.cpp"); ASSERT(Token::simpleMatch(tokenlist.front(), "a + + 1 ; 1 + + b ;")); } }; REGISTER_TEST(TestTokenList) cppcheck-1.82/test/testtype.cpp000066400000000000000000000247471322667425100166160ustar00rootroot00000000000000/* * 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 "checktype.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestType : public TestFixture { public: TestType() : TestFixture("TestType") { } private: void run() { TEST_CASE(checkTooBigShift_Unix32); TEST_CASE(checkIntegerOverflow); TEST_CASE(signConversion); TEST_CASE(longCastAssign); TEST_CASE(longCastReturn); TEST_CASE(checkFloatToIntegerOverflow); } void check(const char code[], Settings* settings = 0, const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); if (!settings) { static Settings _settings; settings = &_settings; } settings->addEnabled("warning"); // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // Check.. CheckType checkType(&tokenizer, settings, this); checkType.runChecks(&tokenizer, settings, this); } void checkTooBigShift_Unix32() { Settings settings; settings.platform(Settings::Unix32); check("int foo(unsigned int x) {\n" " return x << 32;\n" "}",&settings); ASSERT_EQUALS("[test.cpp:2]: (error) Shifting 32-bit value by 32 bits is undefined behaviour\n", errout.str()); check("x = (short)x << 31;",&settings); ASSERT_EQUALS("[test.cpp:1]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour\n", errout.str()); check("int foo(int x) {\n" " return x << 2;\n" "}",&settings); ASSERT_EQUALS("", errout.str()); check("int foo(int x) {\n" " return (long long)x << 40;\n" "}",&settings); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " QList someList;\n" " someList << 300;\n" "}", &settings); ASSERT_EQUALS("", errout.str()); // Ticket #6793 check("template int foo(unsigned int x) { return x << I; }\n" "const unsigned int f = foo<31>(0);\n" "const unsigned int g = foo<100>(0);\n" "template int hoo(unsigned int x) { return x << 32; }\n" "const unsigned int h = hoo<100>(0);", &settings); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting 32-bit value by 32 bits is undefined behaviour\n" "[test.cpp:1]: (error) Shifting 32-bit value by 100 bits is undefined behaviour\n", errout.str()); // #7266: C++, shift in macro check("void f(unsigned int x) {\n" " UINFO(x << 1234);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkIntegerOverflow() { Settings settings; settings.platform(Settings::Unix32); settings.addEnabled("warning"); check("x = (int)0x10000 * (int)0x10000;", &settings); ASSERT_EQUALS("[test.cpp:1]: (error) Signed integer overflow for expression '(int)65536*(int)65536'.\n", errout.str()); check("x = (long)0x10000 * (long)0x10000;", &settings); ASSERT_EQUALS("[test.cpp:1]: (error) Signed integer overflow for expression '(long)65536*(long)65536'.\n", errout.str()); check("void foo() {\n" " int intmax = 0x7fffffff;\n" " return intmax + 1;\n" "}",&settings); ASSERT_EQUALS("[test.cpp:3]: (error) Signed integer overflow for expression 'intmax+1'.\n", errout.str()); check("void foo() {\n" " int intmax = 0x7fffffff;\n" " return intmax - 1;\n" "}",&settings); ASSERT_EQUALS("", errout.str()); check("int foo(signed int x) {\n" " if (x==123456) {}\n" " return x * x;\n" "}",&settings); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'x==123456' is redundant or there is signed integer overflow for expression 'x*x'.\n", errout.str()); check("int foo(signed int x) {\n" " if (x==123456) {}\n" " return -123456 * x;\n" "}",&settings); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'x==123456' is redundant or there is signed integer overflow for expression '-123456*x'.\n", errout.str()); check("int foo(signed int x) {\n" " if (x==123456) {}\n" " return 123456U * x;\n" "}",&settings); ASSERT_EQUALS("", errout.str()); } void signConversion() { check("x = -4 * (unsigned)y;"); ASSERT_EQUALS("[test.cpp:1]: (warning) Suspicious code: sign conversion of -4 in calculation because '-4' has a negative value\n", errout.str()); check("unsigned int f1(signed int x, unsigned int y) {" // x is signed " return x * y;\n" "}\n" "void f2() { f1(-4,4); }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Suspicious code: sign conversion of x in calculation, even though x can have a negative value\n", errout.str()); check("unsigned int f1(int x) {" // x has no signedness, but it can have the value -1 so assume it's signed " return x * 5U;\n" "}\n" "void f2() { f1(-4); }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Suspicious code: sign conversion of x in calculation, even though x can have a negative value\n", errout.str()); check("unsigned int f1(int x) {" // #6168: FP for inner calculation " return 5U * (1234 - x);\n" // <- signed subtraction, x is not sign converted "}\n" "void f2() { f1(-4); }"); ASSERT_EQUALS("", errout.str()); // Don't warn for + and - check("void f1(int x) {" " a = x + 5U;\n" "}\n" "void f2() { f1(-4); }"); ASSERT_EQUALS("", errout.str()); check("size_t foo(size_t x) {\n" " return -2 * x;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious code: sign conversion of -2 in calculation because '-2' has a negative value\n", errout.str()); } void longCastAssign() { Settings settings; settings.addEnabled("style"); settings.platform(Settings::Unix64); check("long f(int x, int y) {\n" " const long ret = x * y;\n" " return ret;\n" "}\n", &settings); ASSERT_EQUALS("[test.cpp:2]: (style) int result is assigned to long variable. If the variable is long to avoid loss of information, then you have loss of information.\n", errout.str()); // typedef check("long f(int x, int y) {\n" " const size_t ret = x * y;\n" " return ret;\n" "}\n", &settings); ASSERT_EQUALS("", errout.str()); // astIsIntResult check("long f(int x, int y) {\n" " const long ret = (long)x * y;\n" " return ret;\n" "}\n", &settings); ASSERT_EQUALS("", errout.str()); } void longCastReturn() { Settings settings; settings.addEnabled("style"); check("long f(int x, int y) {\n" " return x * y;\n" "}\n", &settings); ASSERT_EQUALS("[test.cpp:2]: (style) 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", errout.str()); // typedef check("size_t f(int x, int y) {\n" " return x * y;\n" "}\n", &settings); ASSERT_EQUALS("", errout.str()); } // This function ensure that test works with different compilers. Floats can // be stringified differently. static std::string removeFloat(const std::string& msg) { std::string::size_type pos1 = msg.find("float ("); std::string::size_type pos2 = msg.find(") to integer conversion"); if (pos1 == std::string::npos || pos2 == std::string::npos || pos1 > pos2) return msg; return msg.substr(0,pos1+7) + msg.substr(pos2); } void checkFloatToIntegerOverflow() { check("x = (int)1E100;"); ASSERT_EQUALS("[test.cpp:1]: (error) Undefined behaviour: float () to integer conversion overflow.\n", removeFloat(errout.str())); check("void f(void) {\n" " return (int)1E100;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () to integer conversion overflow.\n", removeFloat(errout.str())); check("void f(void) {\n" " return (int)-1E100;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () to integer conversion overflow.\n", removeFloat(errout.str())); check("void f(void) {\n" " return (short)1E6;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () to integer conversion overflow.\n", removeFloat(errout.str())); check("void f(void) {\n" " return (unsigned char)256.0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () to integer conversion overflow.\n", removeFloat(errout.str())); check("void f(void) {\n" " return (unsigned char)255.5;\n" "}\n"); ASSERT_EQUALS("", removeFloat(errout.str())); check("void f(void) {\n" " char c = 1234.5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () to integer conversion overflow.\n", removeFloat(errout.str())); } }; REGISTER_TEST(TestType) cppcheck-1.82/test/testuninitvar.cpp000066400000000000000000004614441322667425100176530ustar00rootroot00000000000000/* * 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 "checkuninitvar.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include struct InternalError; class TestUninitVar : public TestFixture { public: TestUninitVar() : TestFixture("TestUninitVar") { } private: Settings settings; void run() { LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(uninitvar1); TEST_CASE(uninitvar_decl); // handling various types in C and C++ files TEST_CASE(uninitvar_bitop); // using uninitialized operand in bit operation TEST_CASE(uninitvar_alloc); // data is allocated but not initialized TEST_CASE(uninitvar_arrays); // arrays TEST_CASE(uninitvar_class); // class/struct TEST_CASE(uninitvar_enum); // enum variables TEST_CASE(uninitvar_if); // handling if TEST_CASE(uninitvar_loops); // handling for/while TEST_CASE(uninitvar_switch); // handling switch TEST_CASE(uninitvar_references); // references TEST_CASE(uninitvar_return); // return TEST_CASE(uninitvar_assign); // = {..} TEST_CASE(uninitvar_strncpy); // strncpy doesn't always null-terminate TEST_CASE(func_uninit_var); // analyse function calls for: 'int a(int x) { return x+x; }' TEST_CASE(func_uninit_pointer); // analyse function calls for: 'void a(int *p) { *p = 0; }' TEST_CASE(uninitvar_typeof); // typeof TEST_CASE(uninitvar2); TEST_CASE(uninitvar3); // #3844 TEST_CASE(uninitvar4); // #3869 (reference) TEST_CASE(uninitvar5); // #3861 TEST_CASE(uninitvar2_func); // function calls TEST_CASE(uninitvar2_value); // value flow TEST_CASE(uninitvar2_structmembers); // struct members TEST_CASE(uninitvar2_while); TEST_CASE(uninitvar2_4494); // #4494 TEST_CASE(uninitvar2_malloc); // malloc returns uninitialized data TEST_CASE(uninitvar8); // ticket #6230 TEST_CASE(uninitvar9); // ticket #6424 TEST_CASE(uninitvar_unconditionalTry); TEST_CASE(uninitvar_funcptr); // #6404 TEST_CASE(uninitvar_operator); // #6680 TEST_CASE(uninitvar_ternaryexpression); // #4683 TEST_CASE(uninitvar_pointertoarray); TEST_CASE(uninitvar_cpp11ArrayInit); // #7010 TEST_CASE(uninitvar_rangeBasedFor); // #7078 TEST_CASE(trac_4871); TEST_CASE(syntax_error); // Ticket #5073 TEST_CASE(trac_5970); TEST_CASE(valueFlowUninit); TEST_CASE(isVariableUsageDeref); // *p // dead pointer TEST_CASE(deadPointer); } void checkUninitVar(const char code[], const char fname[] = "test.cpp", bool debugwarnings = false) { // Clear the error buffer.. errout.str(""); // Tokenize.. settings.debugwarnings = debugwarnings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, fname); tokenizer.simplifyTokenList2(); // Check for redundant code.. CheckUninitVar checkuninitvar(&tokenizer, &settings, this); checkuninitvar.check(); settings.debugwarnings = false; settings.experimental = true; } void uninitvar1() { // Ticket #2207 - False negative checkUninitVar("void foo() {\n" " int a;\n" " b = c - a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void foo() {\n" " int a;\n" " b = a - c;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); // Ticket #6455 - some compilers allow const variables to be uninitialized checkUninitVar("void foo() {\n" " const int a;\n" " b = c - a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void foo() {\n" " int *p;\n" " realloc(p,10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("void foo() {\n" // #5240 " char *p = malloc(100);\n" " char *tmp = realloc(p,1000);\n" " if (!tmp) free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo() {\n" " int *p = NULL;\n" " realloc(p,10);\n" "}"); ASSERT_EQUALS("", errout.str()); // dereferencing uninitialized pointer.. checkUninitVar("static void foo()\n" "{\n" " Foo *p;\n" " p->abcd();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " Foo *p;\n" " p->abcd();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("void f(Foo *p)\n" "{\n" " int a;\n" " p->a = malloc(4 * a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int *p;\n" " delete p;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int *p;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int *p;\n" " *p = 135;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int *p;\n" " p[0] = 135;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int *x;\n" " int y = *x;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int *x;\n" " int &y(*x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void foo()\n" "{\n" " int x;\n" " int *y = &x;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " int *x;\n" " int *&y = x;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " int x = xyz::x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " int a;\n" " a = 5 + a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f()\n" "{\n" " int a;\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f()\n" "{\n" " extern int a;\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " int a;\n" " bar(4 * a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int i;\n" " if (i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int i;\n" " for (int x = 0; i < 10; x++);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int i;\n" " for (int x = 0; x < 10; i++);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("static int foo(int x)\n" "{\n" " int i;\n" " if (x)\n" " i = 0;\n" " i++;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int ar[10];\n" " int i;\n" " ar[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("static void foo()\n" "{\n" " int x, y;\n" " x = (y = 10);\n" " int z = y * 2;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void foo() {\n" " int x, y;\n" " x = ((y) = 10);\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3597 checkUninitVar("int f() {\n" " int a;\n" " int b = 1;\n" " (b += a) = 1;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n","", errout.str()); checkUninitVar("int f() {\n" " int a,b,c;\n" " a = b = c;\n" "}", "test.cpp", /*verify=*/ false); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: c\n", "", errout.str()); checkUninitVar("static void foo()\n" "{\n" " Foo p;\n" " p.abcd();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void foo()\n" "{\n" " Foo p;\n" " int x = p.abcd();\n" "}"); ASSERT_EQUALS("", errout.str()); // Unknown types { checkUninitVar("void a()\n" "{\n" " A ret;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a()\n" "{\n" " A ret;\n" " return ret;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Uninitialized variable: ret\n", errout.str()); // #3916 - avoid false positive checkUninitVar("void f(float x) {\n" " union lf { long l; float f; } u_lf;\n" " float hx = (u_lf.f = (x), u_lf.l);\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); } checkUninitVar("void a()\n" "{\n" " int x[10];\n" " int *y = x;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a()\n" "{\n" " int x;\n" " int *y = &x;\n" " *y = 0;\n" " x++;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a()\n" "{\n" " char x[10], y[10];\n" " char *z = x;\n" " memset(z, 0, sizeof(x));\n" " memcpy(y, x, sizeof(x));\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); // Handling >> and << { checkUninitVar("int a() {\n" " int ret;\n" " std::cin >> ret;\n" " ret++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int b) {\n" " int a;\n" " std::cin >> b >> a;\n" " return a;" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int i) {\n" " int a;\n" " i >> a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("int a() {\n" " int ret;\n" " int a = value >> ret;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: ret\n", errout.str()); checkUninitVar("void foo() {\n" // #3707 " Node node;\n" " int x;\n" " node[\"abcd\"] >> x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int a(FArchive &arc) {\n" // #3060 (initialization through operator<<) " int *p;\n" " arc << p;\n" " return *p;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a() {\n" " int ret;\n" " a = value << ret;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: ret\n", errout.str()); // #4320 checkUninitVar("void f() {\n" " int a;\n" " a << 1;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); // #4673 checkUninitVar("void f() {\n" " int a;\n" " std::cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f(std::ostringstream& os) {\n" " int a;\n" " os << a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" " int a;\n" " std::cout << 1 << a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f(std::ostringstream& os) {\n" " int a;\n" " os << 1 << a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); } checkUninitVar("void a() {\n" // asm " int x;\n" " asm();\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a()\n" "{\n" " int x[10];\n" " struct xyz xyz1 = { .x = x };\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a()\n" "{\n" " struct S *s;\n" " s->x = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void foo()\n" "{\n" " char *buf = malloc(100);\n" " struct ABC *abc = buf;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("class Fred {\n" "public:\n" " FILE *f;\n" " ~Fred();\n" "}\n" "Fred::~Fred()\n" "{\n" " fclose(f);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " int c;\n" " ab(sizeof(xyz), &c);\n" " if (c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " int c;\n" " a = (f2(&c));\n" " c++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int a)\n" "{\n" " if (a) {\n" " char *p;\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: p\n", errout.str()); // += checkUninitVar("void f()\n" "{\n" " int c;\n" " c += 2;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void f()\n" "{\n" " int a[10];\n" " a[0] = 10 - a[1];\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", "", errout.str()); // goto/setjmp/longjmp.. checkUninitVar("void foo(int x)\n" "{\n" " long b;\n" " if (g()) {\n" " b =2;\n" " goto found;\n" " }\n" "\n" " return;\n" "\n" "found:\n" " int a = b;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo()\n" "{\n" " jmp_buf env;\n" " int a;\n" " int val = setjmp(env);\n" " if(val)\n" " return a;\n" " a = 1;\n" " longjmp(env, 1);\n" "}"); ASSERT_EQUALS("", errout.str()); // macro_for.. checkUninitVar("int foo()\n" "{\n" " int retval;\n" " if (condition) {\n" " for12(1,2) { }\n" " retval = 1;\n" " }\n" " else\n" " retval = 2;\n" " return retval;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo()\n" "{\n" " int i;\n" " goto exit;\n" " i++;\n" "exit:\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo() {\n" " int x,y=0;\n" "again:\n" " if (y) return x;\n" " x = a;\n" " y = 1;\n" " goto again;\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); // Ticket #3873 (false positive) checkUninitVar("MachineLoopRange *MachineLoopRanges::getLoopRange(const MachineLoop *Loop) {\n" " MachineLoopRange *&Range = Cache[Loop];\n" " if (!Range)\n" " Range = new MachineLoopRange(Loop, Allocator, *Indexes);\n" " return Range;\n" "}"); ASSERT_EQUALS("", errout.str()); // #4040 - False positive checkUninitVar("int f(int x) {\n" " int iter;\n" " {\n" " union\n" " {\n" " int asInt;\n" " double asDouble;\n" " };\n" "\n" " iter = x;\n" " }\n" " return 1 + iter;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); // C++11 style initialization checkUninitVar("int f() {\n" " int i = 0;\n" " int j{ i };\n" " return j;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #5646 checkUninitVar("float foo() {\n" " float source[2] = {3.1, 3.1};\n" " float (*sink)[2] = &source;\n" " return (*sink)[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } // Handling of unknown types. Assume they are POD in C. void uninitvar_decl() { const char code[] = "void f() {\n" " dfs a;\n" " return a;\n" "}"; // Assume dfs is a non POD type if file is C++ checkUninitVar(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); // Assume dfs is a POD type if file is C checkUninitVar(code, "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: a\n", errout.str()); const char code2[] = "struct AB { int a,b; };\n" "void f() {\n" " struct AB ab;\n" " return ab;\n" "}"; checkUninitVar(code2, "test.cpp"); ASSERT_EQUALS("", errout.str()); checkUninitVar(code2, "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Uninitialized variable: ab\n", errout.str()); // Ticket #3890 - False positive for std::map checkUninitVar("void f() {\n" " std::map x;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3906 - False positive for std::vector pointer checkUninitVar("void f() {\n" " std::vector *x = NULL;\n" " return x;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); // Ticket #6701 - Variable name is a POD type according to cfg const char xmldata[] = "\n" "" " " ""; settings.library.loadxmldata(xmldata, sizeof(xmldata)); checkUninitVar("void f() {\n" " Fred _tm;\n" " _tm.dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #7822 - Array type checkUninitVar("A *f() {\n" " A a,b;\n" " b[0] = 0;" " return a;\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); } void uninitvar3() { // #3844 // avoid false positive checkUninitVar("namespace std _GLIBCXX_VISIBILITY(default)\n" "{\n" "_GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n" " typedef unsigned long _Bit_type;\n" " struct _Bit_reference\n" " {\n" " _Bit_type * _M_p;\n" " _Bit_type _M_mask;\n" " _Bit_reference(_Bit_type * __x, _Bit_type __y)\n" " : _M_p(__x), _M_mask(__y) { }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar_bitop() { checkUninitVar("void foo() {\n" " int b;\n" " c = a | b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: b\n", errout.str()); checkUninitVar("void foo() {\n" " int b;\n" " c = b | a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: b\n", errout.str()); } // if.. void uninitvar_if() { checkUninitVar("static void foo()\n" "{\n" " Foo *p;\n" " if (x)\n" " p = new Foo;\n" " p->abcd();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("static void foo(int x)\n" "{\n" " int a;\n" " if (x==1);\n" " if (x==2);\n" " x = a;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("int foo()\n" "{\n" " int i;\n" " if (x)\n" " i = 22;\n" " else\n" " i = 33;\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo(int x)\n" // #5503 "{\n" " int i;\n" " if (x < 2)\n" " i = 22;\n" " else if (x >= 2)\n" // condition is always true " i = 33;\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo()\n" "{\n" " int i;\n" " if (x)\n" " i = 22;\n" " else\n" " {\n" " char *y = {0};\n" " i = 33;\n" " }\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo()\n" "{\n" " int i;\n" " if (x)\n" " {\n" " struct abc abc1 = (struct abc) { .a=0, .b=0, .c=0 };\n" " i = 22;\n" " }\n" " else\n" " {\n" " i = 33;\n" " }\n" " return i;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void foo(int x)\n" "{\n" " Foo *p;\n" " if (x)\n" " p = new Foo;\n" " if (x)\n" " p->abcd();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo(int a)\n" "{\n" " int n;\n" " int condition;\n" " if(a == 1) {\n" " n=0;\n" " condition=0;\n" " }\n" " else {\n" " n=1;\n" " }\n" "\n" " if( n == 0) {\n" " a=condition;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " C *c;\n" " if (fun(&c));\n" " c->Release();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " C c;\n" " if (fun(&c.d));\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char a[10];\n" " if (a[0] = x){}\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo(int x)\n" "{\n" " int i;\n" " if (one())\n" " i = 1;\n" " else\n" " return 3;\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2207 - False positive checkUninitVar("void foo(int x) {\n" " int a;\n" " if (x)\n" " a = 1;\n" " if (!x)\n" " return;\n" " b = (c - a);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo()\n" "{\n" " int ret;\n" " if (one())\n" " ret = 1;\n" " else\n" " throw 3;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int a)\n" "{\n" " int ret;\n" " if (a == 1)\n" " ret = 1;\n" " else\n" " XYZ ret = 2;\n" // XYZ may be an unexpanded macro so bailout the checking of "ret". " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Uninitialized variable: ret\n", errout.str()); checkUninitVar("int f(int a, int b)\n" "{\n" " int x;\n" " if (a)\n" " x = a;\n" " else {\n" " do { } while (f2());\n" " x = b;\n" " }\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo(long verbose,bool bFlag)\n" "{\n" " double t;\n" " if (bFlag)\n" " {\n" " if (verbose)\n" " t = 1;\n" " if (verbose)\n" " std::cout << (12-t);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int test(int cond1, int cond2) {\n" " int foo;\n" " if (cond1 || cond2) {\n" " if (cond2)\n" " foo = 0;\n" " }\n" " if (cond2) {\n" " int t = foo*foo;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // ? : checkUninitVar("static void foo(int v) {\n" " int x;\n" " x = v <= 0 ? -1 : x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void foo()\n" "{\n" " const char *msgid1, *msgid2;\n" " int ret = bar(&msgid1);\n" " if (ret > 0) {\n" " ret = bar(&msgid2);\n" " }\n" " ret = ret <= 0 ? -1 :\n" " strcmp(msgid1, msgid2) == 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo(int a, int b)\n" "{\n" " int x; x = (anext())\n" " {\n" " }\n" " } while (tok2);\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2226: C++0x loop checkUninitVar("void f() {\n" " container c;\n" " for (iterator it : c) {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2345: False positive in sub-condition in if inside a loop checkUninitVar("void f(int x) {\n" " const PoolItem* pItem;\n" " while (x > 0) {\n" " if (GetItem(&pItem) && (*pItem != rPool))\n" " { }\n" " x--;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " const PoolItem* pItem;\n" " while (x > 0) {\n" " if (*pItem != rPool)\n" " { }\n" " x--;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: pItem\n", errout.str()); // #2231 - conditional initialization in loop.. checkUninitVar("int foo(char *a) {\n" " int x;\n" "\n" " for (int i = 0; i < 10; ++i) {\n" " if (a[i] == 'x') {\n" " x = i;\n" " break;\n" " }\n" " }\n" "\n" " return x;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:11]: (error) Uninitialized variable: x\n", "", errout.str()); // Ticket #2796 checkUninitVar("void foo() {\n" " while (true) {\n" " int x;\n" " if (y) x = 0;\n" " else break;\n" " return x;\n" // <- x is initialized " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Assignment in for. Ticket #3287 checkUninitVar("int foo(char* in, bool b) {\n" " char* c;\n" " if (b) for (c = in; *c == 0; ++c) {}\n" " else c = in + strlen(in) - 1;\n" " *c = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } // switch.. void uninitvar_switch() { checkUninitVar("void f(int x)\n" "{\n" " short c;\n" " switch(x) {\n" " case 1:\n" " c++;\n" " break;\n" " };\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: c\n", "", errout.str()); checkUninitVar("char * f()\n" "{\n" " static char ret[200];\n" " memset(ret, 0, 200);\n" " switch (x)\n" " {\n" " case 1: return ret;\n" " case 2: return ret;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo(const int iVar, unsigned int slot, unsigned int pin)\n" "{\n" " int i;\n" " if (iVar == 0)\n" " {\n" " switch (slot)\n" " {\n" " case 4: return 5;\n" " default: return -1;\n" " }\n" " }\n" " else\n" " {\n" " switch (pin)\n" " {\n" " case 0: i = 2; break;\n" " default: i = -1; break;\n" " }\n" " }\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); // #1855 - switch(foo(&x)) checkUninitVar("int a()\n" "{\n" " int x;\n" " switch (foo(&x))\n" " {\n" " case 1:\n" " return x;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3231 - ({ switch .. }) checkUninitVar("void f() {\n" " int a;\n" " ({\n" " switch(sizeof(int)) {\n" " case 4:\n" " default:\n" " (a)=0;\n" " break;\n" " };\n" " })\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); } // arrays.. void uninitvar_arrays() { checkUninitVar("int f()\n" "{\n" " char a[10];\n" " a[a[0]] = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", "", errout.str()); checkUninitVar("int f()\n" "{\n" " char a[10];\n" " *a = '\\0';\n" " int i = strlen(a);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " char a, b[10];\n" " a = b[0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " char a[10], b[10];\n" " a[0] = b[0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " char a[10], *p;\n" " *(p = a) = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char a[10], *p;\n" " p = &(a[10]);\n" "}"); ASSERT_EQUALS("", errout.str()); // array usage in ?: (tests that the isVariableUsed() works) checkUninitVar("void f() {\n" " char a[10], *p;\n" " p = c?a:0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char a[10], c;\n" " c = *(x?a:0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" " char a[10], c;\n" " strcpy(dest, x?a:\"\");\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); checkUninitVar("void f(int x) {\n" " int a[2];\n" " y *= (x ? 1 : 2);\n" "}"); ASSERT_EQUALS("", errout.str()); // passing array to library functions checkUninitVar("void f()\n" "{\n" " char c[50] = \"\";\n" " strcat(c, \"test\");\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char s[20];\n" " strcpy(s2, s);\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void f() {\n" " char s[20];\n" " strcat(s, \"abc\");\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void f() {\n" " char s[20];\n" " strchr(s, ' ');\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void foo()\n" "{\n" " int y[2];\n" " int s;\n" " GetField( y + 0, y + 1 );\n" " s = y[0]*y[1];\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " int a[2];\n" " init(a - 1);\n" " int b = a[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " Fred a[2];\n" " Fred b = a[0];\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2320 checkUninitVar("void foo() {\n" " char a[2];\n" " char *b = (a+2) & 7;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // Ticket #3050 " char a[2];\n" " printf(\"%s\", a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", "", errout.str()); checkUninitVar("void f() {\n" // Ticket #5108 (fp) " const char *a;\n" " printf(\"%s\", a=\"abc\");\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // Ticket #3497 " char header[1];\n" " *((unsigned char*)(header)) = 0xff;\n" " return header[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // Ticket #3497 " char header[1];\n" " *((unsigned char*)((unsigned char *)(header))) = 0xff;\n" " return header[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " ABC abc;\n" " int a[1];\n" "\n" " abc.a = a;\n" " init(&abc);\n" " return a[0];\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3344 checkUninitVar("void f(){\n" " char *strMsg = \"This is a message\";\n" " char *buffer=(char*)malloc(128*sizeof(char));\n" " strcpy(strMsg,buffer);\n" " free(buffer);\n" "}", "test.cpp", false); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: buffer\n", errout.str()); // #3845 checkUninitVar("int foo() {\n" " int a[1] = {5};\n" " return a[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo() {\n" " int a[2][2] = {{3,4}, {5,6}};\n" " return a[0][1];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo() {\n" " int a[1];\n" " return a[0];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("int foo() {\n" " int a[2][2];\n" " return a[0][1];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("int foo() {\n" " int a[10];\n" " dostuff(a[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); // # 4740 checkUninitVar("void f() {\n" " int *a[2][19];\n" " int **b = a[0];\n" "}"); ASSERT_EQUALS("", errout.str()); // #6869 - FP when passing uninit array to function checkUninitVar("void bar(PSTR x);\n" "void foo() {\n" " char x[10];\n" " bar(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // struct checkUninitVar("struct Fred { int x; int y; };\n" "" "void f() {\n" " struct Fred fred[10];\n" " fred[1].x = 0;\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); } void uninitvar_pointertoarray() { checkUninitVar("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"); // kind of regression test - there are more lines which access vertices!? ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: vertices\n" "[test.cpp:5]: (error) Uninitialized variable: vertices\n" "[test.cpp:6]: (error) Uninitialized variable: vertices\n" "[test.cpp:7]: (error) Uninitialized variable: vertices\n" "[test.cpp:8]: (error) Uninitialized variable: vertices\n" "[test.cpp:9]: (error) Uninitialized variable: vertices\n" "[test.cpp:10]: (error) Uninitialized variable: vertices\n" "[test.cpp:11]: (error) Uninitialized variable: vertices\n" "[test.cpp:18]: (error) Uninitialized variable: vertices\n", errout.str()); } void uninitvar_cpp11ArrayInit() { // #7010 checkUninitVar("double foo(bool flag) {\n" " double adIHPoint_local[4][4]{};\n" " function(*adIHPoint_local);\n" "}"); ASSERT_EQUALS("", errout.str()); } // alloc.. void uninitvar_alloc() { checkUninitVar("void f() {\n" " char *s = malloc(100);\n" " strcat(s, \"abc\");\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory is allocated but not initialized: s\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char *s1 = new char[10];\n" " char *s2 = new char[strlen(s1)];\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: s1\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char *p = malloc(64);\n" " int x = p[0];\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: p\n", "", errout.str()); checkUninitVar("void f() {\n" " char *p = malloc(64);\n" " if (p[0]) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("void f() {\n" " char *p = malloc(64);\n" " return p[0];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("void f()\n" "{\n" " Fred *fred = new Fred;\n" " fred->foo();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct Fred { int i; Fred(int, float); };\n" "void f() {\n" " Fred *fred = new Fred(1, 2);\n" " fred->foo();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " Fred *fred = malloc(sizeof(Fred));\n" " x(&fred->f);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " Fred *fred = malloc(sizeof(Fred));\n" " x(fred->f);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo(char *s)\n" "{\n" " char *a = malloc(100);\n" " *a = *s;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " char *a;\n" " if (a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void foo()\n" "{\n" " char *a = malloc(100);\n" " if (a);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " ABC *abc = malloc(100);\n" " abc->a = 123;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " ABC *abc = malloc(100);\n" " abc->a.word = 123;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " ABC *abc = malloc(100);\n" " abc->a = 123;\n" " abc->a += 123;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " ABC *abc = malloc(100);\n" " free(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " char *s = malloc(100);\n" " if (!s)\n" " return;\n" " char c = *s;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Memory is allocated but not initialized: s\n", "", errout.str()); // #3708 - false positive when using ptr typedef checkUninitVar("void f() {\n" " uintptr_t x = malloc(100);\n" " uintptr_t y = x + 10;\n" // <- not bad usage "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " z_stream strm;\n" " char* buf = malloc(10);\n" " strm.next_out = buf;\n" " deflate(&strm, Z_FINISH);\n" " memcpy(body, buf, 10);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6451 - allocation in subscope checkUninitVar("struct StgStrm {\n" " StgIo& rIo;\n" " StgStrm(StgIo&);\n" " virtual sal_Int32 Write();\n" "};\n" "void Tmp2Strm() {\n" " StgStrm* pNewStrm;\n" " if (someflag)\n" " pNewStrm = new StgStrm(rIo);\n" " else\n" " pNewStrm = new StgStrm(rIo);\n" " pNewStrm->Write();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct StgStrm {\n" " StgIo& rIo;\n" " StgStrm(StgIo&);\n" " virtual sal_Int32 Write();\n" "};\n" "void Tmp2Strm() {\n" " StgStrm* pNewStrm;\n" " if (someflag)\n" " pNewStrm = new StgStrm(rIo);\n" " pNewStrm->Write();\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:10]: (error) Uninitialized variable: pNewStrm\n", "", errout.str()); // #6450 - calling a member function is allowed if memory was allocated by new checkUninitVar("struct EMFPFont {\n" " bool family;\n" " void Initialize();\n" "};\n" "void processObjectRecord() {\n" " EMFPFont *font = new EMFPFont();\n" " font->Initialize();\n" "}"); ASSERT_EQUALS("", errout.str()); // #7623 - new can also initialize the memory, don't warn in this case checkUninitVar("void foo(){\n" " int* p1 = new int(314); \n" " int* p2 = new int(); \n" " int* arr = new int[5](); \n" " std::cout << *p1 << *p2 << arr[0]; \n" "}"); ASSERT_EQUALS("", errout.str()); } // class / struct.. void uninitvar_class() { checkUninitVar("class Fred\n" "{\n" " int i;\n" " int a() { return i; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " struct Relative {\n" " Surface *surface;\n" " void MoveTo(int x, int y) {\n" " surface->MoveTo();\n" " }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " static const struct ab {\n" " int a,b;\n" " int get_a() { return a; }" " } = { 0, 0 };\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " int i;\n" " {\n" " union ab {\n" " int a,b;\n" " }\n" " i = 0;\n" " }\n" " return i;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " struct AB ab;\n" " x = ab.x = 12;\n" "}"); ASSERT_EQUALS("", errout.str()); } // enum.. void uninitvar_enum() { checkUninitVar("void f()\n" "{\n" " enum AB { a, b };\n" " AB ab;\n" " if (ab);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: ab\n", errout.str()); } // references.. void uninitvar_references() { checkUninitVar("void f()\n" "{\n" " int a;\n" " int &b = a;\n" " b = 0;\n" " int x = a;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(struct blame_entry *ent)\n" "{\n" " struct origin *suspect = ent->suspect;\n" " char hex[41];\n" " strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " const std::string s(x());\n" " strchr(s.c_str(), ',');\n" "}"); ASSERT_EQUALS("", errout.str()); // #6717 checkUninitVar("void f() {\n" " struct thing { int value; };\n" " thing it;\n" " int& referenced_int = it.value;\n" " referenced_int = 123;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar_return() { checkUninitVar("static int foo() {\n" " int ret;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: ret\n", errout.str()); checkUninitVar("static int foo() {\n" " int ret;\n" " return ret+5;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: ret\n", errout.str()); checkUninitVar("static int foo() {\n" " int ret;\n" " return ret = 5;\n" "}"); ASSERT_EQUALS("", errout.str()); { checkUninitVar("static int foo() {\n" " int ret;\n" " return cin >> ret;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static int foo() {\n" " int ret;\n" " return cin >> ret;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: ret\n", errout.str()); } // Ticket #6341- False negative { checkUninitVar("wchar_t f() { int i; return btowc(i); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("wchar_t f(int i) { return btowc(i); }"); ASSERT_EQUALS("", errout.str()); // Avoid a potential false positive (#6341) checkUninitVar( "void setvalue(int &x) {\n" " x = 0;\n" " return 123;\n" "}\n" "int f() {\n" " int x;\n" " return setvalue(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #5412 - False negative { checkUninitVar("void f(bool b) {\n" " double f;\n" " if (b) { }\n" " else {\n" " f = 0.0;\n" " }\n" " printf (\"%f\",f);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: f\n", errout.str()); // Check for potential FP checkUninitVar("void f(bool b) {\n" " double f;\n" " if (b) { f = 1.0 }\n" " else {\n" " f = 0.0;\n" " }\n" " printf (\"%f\",f);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #2146 - False negative checkUninitVar("int f(int x) {\n" " int y;\n" " return x ? 1 : y;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: y\n", errout.str()); // Ticket #3106 - False positive { checkUninitVar("int f() {\n" " int i;\n" " return x(&i) ? i : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f() {\n" " int i;\n" " return x() ? i : 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: i\n", errout.str()); } } void uninitvar_assign() { // = { .. } // #1533 checkUninitVar("char a()\n" "{\n" " char key;\n" " struct A msg = { .buf = {&key} };\n" " init(&msg);\n" " key++;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #5660 - False positive checkUninitVar("int f() {\n" " int result;\n" " int *res[] = {&result};\n" " foo(res);\n" " return result;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6873 checkUninitVar("int f() {\n" " char a[10];\n" " char *b[] = {a};\n" " foo(b);\n" " return atoi(a);\n" "}"); ASSERT_EQUALS("", errout.str()); // = { .. } checkUninitVar("int f() {\n" " int a;\n" " int *p[] = { &a };\n" " *p[0] = 0;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } // strncpy doesn't always null-terminate.. void uninitvar_strncpy() { // TODO: Add this checking // Can it be added without hardcoding? checkUninitVar("void f()\n" "{\n" " char a[100];\n" " strncpy(a, s, 20);\n" " strncat(a, s, 20);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous usage of 'a' (strncpy doesn't always null-terminate it).\n", "", errout.str()); checkUninitVar("void f()\n" "{\n" " char a[100];\n" " strncpy(a, \"hello\", 3);\n" " strncat(a, \"world\", 20);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous usage of 'a' (strncpy doesn't always null-terminate it).\n", "", errout.str()); checkUninitVar("void f()\n" "{\n" " char a[100];\n" " strncpy(a, \"hello\", sizeof(a));\n" " strncat(a, \"world\", 20);\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); // #3245 - false positive { checkUninitVar("void f() {\n" " char a[100];\n" " strncpy(a,p,10);\n" " memcmp(a,q,10);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char a[100];\n" " strncpy(a,p,10);\n" " if (memcmp(a,q,10)==0);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Using strncpy isn't necessarily dangerous usage checkUninitVar("void f(const char dev[], char *str) {\n" " char buf[10];\n" " strncpy(buf, dev, 10);\n" " strncpy(str, buf, 10);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char dst[4];\n" " const char* source = \"You\";\n" " strncpy(dst, source, sizeof(dst));\n" " char value = dst[2];\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); } // valid and invalid use of 'int a(int x) { return x + x; }' void func_uninit_var() { const std::string funca("int a(int x) { return x + x; }"); checkUninitVar((funca + "void b() {\n" " int x;\n" " a(x);\n" "}").c_str()); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar((funca + "void b() {\n" " int *p;\n" " a(*p);\n" "}").c_str()); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: p\n", errout.str()); } // valid and invalid use of 'void a(int *p) { *p = 0; }' void func_uninit_pointer() { const std::string funca("void a(int *p) { *p = 0; }"); // ok - initialized pointer checkUninitVar((funca + "void b() {\n" " int buf[10];\n" " a(buf);\n" "}").c_str()); ASSERT_EQUALS("", errout.str()); // not ok - uninitialized pointer checkUninitVar((funca + "void b() {\n" " int *p;\n" " a(p);\n" "}").c_str()); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: p\n", errout.str()); } void uninitvar_typeof() { checkUninitVar("void f() {\n" " struct Fred *fred;\n" " typeof(fred->x);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " struct SData * s;\n" " ab(typeof(s->status));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " struct SData * s;\n" " TYPEOF(s->status);\n" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:3]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void f() {\n" " int *n = ({ typeof(*n) z; (typeof(*n)*)z; })\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); } void uninitvar2() { // using uninit var checkUninitVar("void f() {\n" " int x;\n" " x++;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " str[x] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " int y = x & 3;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " int y = 3 & x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " x = 3 + x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("int f() {\n" " int x;\n" " x = x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " struct ABC *abc;\n" " abc->a = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: abc\n", errout.str()); checkUninitVar("int f() {\n" " static int x;\n" " return ++x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f() {\n" " extern int x;\n" " return ++x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #3926 - weird cast. " int x;\n" " *(((char *)&x) + 0) = 0;\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #4737 - weird cast. " int x;\n" " do_something(&((char*)&x)[0], 1);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " char *p = (char*)&x + 1;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int i;\n" " i=f(), i!=2;\n" "}"); ASSERT_EQUALS("", errout.str()); // using uninit var in condition checkUninitVar("void f(void) {\n" " int x;\n" " if (x) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " if (1 == (3 & x)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); // ?: checkUninitVar("int f(int *ptr) {\n" " int a;\n" " int *p = ptr ? ptr : &a;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int a) {\n" " int x;\n" " if (a==3) { x=2; }\n" " y = (a==3) ? x : a;\n" "}"); ASSERT_EQUALS("", errout.str()); // = ({ .. }) checkUninitVar("void f() {\n" " int x = ({ 1 + 2; });\n" " int y = 1 + (x ? y : y);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: y\n", errout.str()); // >> => initialization / usage { const char code[] = "void f() {\n" " int x;\n" " if (i >> x) { }\n" "}"; checkUninitVar(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); checkUninitVar(code, "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: x\n", errout.str()); } checkUninitVar("void f() {\n" " int i, i2;\n" " strm >> i >> i2;\n" "}"); ASSERT_EQUALS("", errout.str()); // unconditional initialization checkUninitVar("int f() {\n" " int ret;\n" " if (a) { ret = 1; }\n" " else { {} ret = 2; }\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f() {\n" " int ret;\n" " if (a) { ret = 1; }\n" " else { s=foo(1,{2,3},4); ret = 2; }\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); // conditional initialization checkUninitVar("void f() {\n" " int x;\n" " if (y == 1) { x = 1; }\n" " else { if (y == 2) { x = 1; } }\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " if (y == 1) { x = 1; }\n" " else { if (y == 2) { x = 1; } }\n" " if (y == 3) { }\n" // <- ignore condition " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: x\n", errout.str()); // initialization in condition checkUninitVar("void f() {\n" " int a;\n" " if (init(&a)) { }\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); // return, break, continue, goto checkUninitVar("void f() {\n" " int x;\n" " if (y == 1) { return; }\n" " else { x = 1; }\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " if (y == 1) { return; }\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("int f(int x) {\n" " int ret;\n" " if (!x) {\n" " ret = -123;\n" " goto out1;\n" " }\n" " return 0;\n" "out1:\n" "out2:\n" " return ret;\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int i;\n" " if (x) {\n" " i = 1;\n" " } else {\n" " goto out;\n" " }\n" " i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f() {\n" " int i,x;\n" " for (i=0;i<9;++i)\n" " if (foo) break;\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("int f() {\n" " int x;\n" " while (foo)\n" " if (bar) break;\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); // try/catch : don't warn about exception variable checkUninitVar("void f() {\n" " try {\n" " } catch (CException* e) {\n" " trace();\n" " e->Delete();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #5347 " try {\n" " } catch (const char* e) {\n" " A a = e;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // exit checkUninitVar("void f() {\n" " int x;\n" " if (y == 1) { exit(0); }\n" " else { x = 1; }\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); // strange code.. don't crash (#3415) checkUninitVar("void foo() {\n" " int i;\n" " ({ if (0); });\n" " for_each(i) { }\n" "}", "test.c", false); // if, if checkUninitVar("void f(int a) {\n" " int i;\n" " if (a) i = 0;\n" " if (a) i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int a,b=0;\n" " if (x) {\n" " if (y) {\n" " a = 0;\n" " b = 1;\n" " }\n" " }\n" " if (b) a++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int a=0, b;\n" " if (x) { }\n" " else { if (y==2) { a=1; b=2; } }\n" " if (a) { ++b; }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void f(int x, int y) {\n" " int a;\n" " if (x == 0) { a = y; }\n" " if (x == 0 && (a == 1)) { }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void f() {\n" " int a=0, b;\n" " if (something) { a = dostuff(&b); }\n" " if (!a || b) { }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void f(int x, int y) {\n" " int a;\n" " if (x == 0 && (a == 1)) { }\n" "}", "test.cpp", false); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" " int a;\n" " if (x) { a = 0; }\n" " if (x) { if (y) { a++; } }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int a;\n" " if (x) { a = 0; }\n" " if (x) { if (y) { } else { a++; } }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " if (x) ab = getAB();\n" " else ab.a = 0;\n" " if (ab.a == 1) b = ab.b;\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(void) {\n" " int a;\n" " int i;\n" " if (x) { noreturn(); }\n" " else { i = 0; }\n" " if (i==1) { a = 0; }\n" " else { a = 1; }\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int a) {\n" // #4560 " int x = 0, y;\n" " if (a) x = 1;\n" " else return 0;\n" " if (x) y = 123;\n" // <- y is always initialized " else {}\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int a) {\n" // #6583 " int x;\n" " if (a < 2) exit(1);\n" " else if (a == 2) x = 0;\n" " else exit(2);\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int a) {\n" // #4560 " int x = 1, y;\n" " if (a) x = 0;\n" " else return 0;\n" " if (x) {}\n" " else y = 123;\n" // <- y is always initialized " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" // #3948 " int value;\n" " if (x !=-1)\n" " value = getvalue();\n" " if (x == -1 || value > 300) {}\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " int value;\n" " if (x == 32)\n" " value = getvalue();\n" " if (x == 1)\n" " v = value;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: value\n", errout.str()); checkUninitVar("void f(int x) {\n" " int value;\n" " if (x == 32)\n" " value = getvalue();\n" " if (x == 32) {}\n" " else v = value;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: value\n", errout.str()); checkUninitVar("static int x;" // #4773 "int f() {\n" " int y;\n" " if (x) g();\n" " if (x) y = 123;\n" " else y = 456;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static int x;" // #4773 "int f() {\n" " int y;\n" " if (!x) g();\n" " if (x) y = 123;\n" " else y = 456;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int a) {\n" " int x;\n" " if (a) x=123;\n" " if (!a) {\n" " if (!a) {}\n" " else if (x) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // asm checkUninitVar("void f() {\n" " int x;\n" " asm();\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); // sizeof / typeof / offsetof / etc checkUninitVar("void f() {\n" " int i;\n" " sizeof(i+1);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int i;\n" " if (100 == sizeof(i+1));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " struct ABC *abc;\n" " int i = ARRAY_SIZE(abc.a);" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int *abc;\n" " typeof(*abc);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " struct ABC *abc;\n" " return do_something(typeof(*abc));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " A *a;\n" " a = malloc(sizeof(*a));\n" "}"); ASSERT_EQUALS("", errout.str()); // & checkUninitVar("void f() {\n" // #4426 - address of uninitialized variable " int a,b;\n" " if (&a == &b);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #4439 - cast address of uninitialized variable " int a;\n" " x((LPARAM)(RECT*)&a);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #4778 - cast address of uninitialized variable " long a;\n" " &a;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #4717 - ({}) " int a = ({ long b = (long)(123); 2 + b; });\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); } // #3869 - reference variable void uninitvar4() { checkUninitVar("void f() {\n" " int buf[10];\n" " int &x = buf[0];\n" " buf[0] = 0;\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); } // #3861 void uninitvar5() { // ensure there is no false positive checkUninitVar("void f() {\n" " x c;\n" " c << 2345;\n" "}"); ASSERT_EQUALS("", errout.str()); // ensure there is no false negative checkUninitVar("void f() {\n" " char c;\n" " char a = c << 2;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: c\n", errout.str()); // #4320 checkUninitVar("void f() {\n" " int a;\n" " a << 1;\n" // there might be a operator<< "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar8() { const char code[] = "struct Fred {\n" " void Sync(dsmp_t& type, int& len, int limit = 123);\n" " void Sync(int& syncpos, dsmp_t& type, int& len, int limit = 123);\n" " void FindSyncPoint();\n" "};\n" "void Fred::FindSyncPoint() {\n" " dsmp_t type;\n" " int syncpos, len;\n" " Sync(syncpos, type, len);\n" "}"; checkUninitVar(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); } void uninitvar9() { // 6424 const char code[] = "namespace Ns { class C; }\n" "void f1() { char *p; *p = 0; }\n" "class Ns::C* p;\n" "void f2() { char *p; *p = 0; }"; checkUninitVar(code, "test.cpp"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: p\n" "[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); } void uninitvar_unconditionalTry() { // Unconditional scopes and try{} scopes checkUninitVar("int f() {\n" " int i;\n" " {\n" " return i;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("int f() {\n" " int i;\n" " try {\n" " return i;\n" " } catch(...) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); } void uninitvar_funcptr() { checkUninitVar("void getLibraryContainer() {\n" " Reference< XStorageBasedLibraryContainer >(*Factory)(const Reference< XComponentContext >&, const Reference< XStorageBasedDocument >&)\n" " = &DocumentDialogLibraryContainer::create;\n" " rxContainer.set((*Factory)(m_aContext, xDocument));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void getLibraryContainer() {\n" " void* x;\n" " Reference< XStorageBasedLibraryContainer >(*Factory)(const Reference< XComponentContext >&, const Reference< XStorageBasedDocument >&)\n" " = x;\n" " rxContainer.set((*Factory)(m_aContext, xDocument));\n" "}", "test.cpp", false); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void getLibraryContainer() {\n" " Reference< XStorageBasedLibraryContainer >(*Factory)(const Reference< XComponentContext >&, const Reference< XStorageBasedDocument >&);\n" " rxContainer.set((*Factory)(m_aContext, xDocument));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: Factory\n", errout.str()); } void uninitvar_operator() { // Ticket #6463, #6680 checkUninitVar("struct Source { Source& operator>>(int& i) { i = 0; return *this; } };\n" "struct Sink { int v; };\n" "Source foo;\n" "void uninit() {\n" " Sink s;\n" " int n = 1 >> s.v;\n" // Not initialized "};\n" "void notUninit() {\n" " Sink s1;\n" " foo >> s1.v;\n" // Initialized by operator>> " Sink s2;\n" " int n;\n" " foo >> s2.v >> n;\n" // Initialized by operator>> "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized struct member: s.v\n", errout.str()); checkUninitVar("struct Fred { int a; };\n" "void foo() {\n" " Fred fred;\n" " std::cin >> fred.a;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Handling of function calls void uninitvar2_func() { // non-pointer variable checkUninitVar("void a(char);\n" // value => error "void b() {\n" " char c;\n" " a(c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(char c);\n" // value => error "void b() {\n" " char c;\n" " a(c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(const char c);\n" // const value => error "void b() {\n" " char c;\n" " a(c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(char *c);\n" // address => no error "void b() {\n" " char c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a(pstr s);\n" // address => no error "void b() {\n" " char c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a(const char *c);\n" // const address => error "void b() {\n" " char c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); // pointer variable checkUninitVar("void a(char c);\n" // value => error "void b() {\n" " char *c;\n" " a(*c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(const char c);\n" // const value => error "void b() {\n" " char c;\n" " a(*c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(char *c);\n" // address => error "void b() {\n" " char *c;\n" " a(c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("typedef struct { int a; int b; } AB;\n" "void a(AB *ab);\n" "void b() {\n" " AB *ab;\n" " a(ab);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: ab\n", errout.str()); checkUninitVar("void a(const char *c);\n" // const address => error "void b() {\n" " char *c;\n" " a(c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(const char c[]);\n" // const address => error "void b() {\n" " char *c;\n" " a(c);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void a(char **c);\n" // address of pointer => no error "void b() {\n" " char *c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a(char *c);\n" // address of pointer (suspicious cast to pointer) => no error "void b() {\n" " char *c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a(const char **c);\n" // const address of pointer => no error "void b() {\n" " const char *c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); // array checkUninitVar("int calc(const int *p, int n);\n" "void f() {\n" " int x[10];\n" " calc(x,10);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", "", errout.str()); checkUninitVar("void f() {\n" " int x[10];\n" " int &x0(*x);\n" "}"); ASSERT_EQUALS("", errout.str()); // .... checkUninitVar("struct ABC { int a; };\n" // struct initialization "void clear(struct ABC &abc);\n" "int f() {\n" " struct ABC abc;\n" " clear(abc);\n" " return abc.a;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void write_packet() {\n" " time_t now0;\n" " time(&now0);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void write_packet() {\n" " time_t* now0;\n" " time(now0);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: now0\n", errout.str()); checkUninitVar("void write_packet() {\n" " char now0;\n" " strcmp(&now0, sth);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: now0\n", errout.str()); // #2775 - uninitialized struct pointer in subfunction checkUninitVar("void a(struct Fred *fred) {\n" " fred->x = 0;\n" "}\n" "\n" "void b() {\n" " struct Fred *p;\n" " a(p);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: p\n", errout.str()); // #2946 - FP array is initialized in subfunction checkUninitVar("void a(char *buf) {\n" " buf[0] = 0;\n" "}\n" "void b() {\n" " char buf[10];\n" " a(buf);\n" " buf[1] = buf[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar2_value() { checkUninitVar("void f() {\n" " int i;\n" " if (x) {\n" " int y = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated " if (y != 0) return;\n" " i++;\n" " }\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int i, y;\n" " if (x) {\n" " y = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated " if (y != 0) return;\n" " i++;\n" " }\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int i, y;\n" " if (x) y = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated " else y = get_value(i);\n" " if (y != 0) return;\n" // <- condition is always true if i is uninitialized " i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " int i;\n" " if (!x) i = 0;\n" " if (!x || i>0) {}\n" // <- error "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", "", errout.str()); checkUninitVar("void f(int x) {\n" " int i;\n" " if (x) i = 0;\n" " if (!x || i>0) {}\n" // <- no error "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " int i;\n" " if (!x) { }\n" " else i = 0;\n" " if (x || i>0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("void f(int x) {\n" " int i;\n" " if (x) { }\n" " else i = 0;\n" " if (x || i>0) {}\n" // <- no error "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int x) {\n" " int y;\n" " if (x) y = do_something();\n" " if (!x) return 0;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int x) {\n" // FP with ?: " int a;\n" " if (x)\n" " a = p;\n" " return x ? 2*a : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int x) {\n" " int a;\n" " if (x)\n" " a = p;\n" " return y ? 2*a : 3*a;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" // Don't crash " int a;\n" " dostuff(\"ab\" cd \"ef\", x?a:z);\n" // <- No AST is created for ?: "}"); // Unknown => bail out.. checkUninitVar("void f(int x) {\n" " int i;\n" " if (a(x)) i = 0;\n" " if (b(x)) return;\n" " i++;\n" // <- no error if b(x) is always true when a(x) is false "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " int i;\n" " if (x) i = 0;\n" " while (condition) {\n" " if (x) i++;\n" // <- no error " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(int x) {\n" " int i;\n" " if (x) i = 0;\n" " while (condition) {\n" " i++;\n" " }\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void uninitvar2_structmembers() { // struct members checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " int a = ab.a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " AB ab;\n" " int a = ab.a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = ab.a + 1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void do_something(const struct AB ab);\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 0;\n" " do_something(ab);\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:6]: (error) Uninitialized struct member: ab.b\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" // #4760 "void do_something(int a);\n" "void f(void) {\n" " struct AB ab;\n" " do_something(ab.a);\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:5]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " int a = ab.a;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " buf[ab.a] = 0;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 1;\n" " x = ab;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:5]: (error) Uninitialized struct member: ab.b\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 1;\n" " x = *(&ab);\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:5]: (error) Uninitialized struct member: ab.b\n", errout.str()); checkUninitVar("class Element {\n" " static void f() { }\n" "};\n" "void test() {\n" " Element *element; element->f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " static void f() { }\n" "};\n" "void test() {\n" " Element *element; (*element).f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " static int v;\n" "};\n" "void test() {\n" " Element *element; element->v;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " static int v;\n" "};\n" "void test() {\n" " Element *element; (*element).v;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " void f() { }\n" "};\n" "void test() {\n" " Element *element; element->f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " void f() { }\n" "};\n" "void test() {\n" " Element *element; (*element).f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " int v;\n" "};\n" "void test() {\n" " Element *element; element->v;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("class Element {\n" " int v;\n" "};\n" "void test() {\n" " Element *element; (*element).v;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: element\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" // pass struct member by address "void f(void) {\n" " struct AB ab;\n" " assign(&ab.a, 0);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void do_something(const struct AB ab);\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 0;\n" " ab.b = 0;\n" " do_something(ab);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); { checkUninitVar("struct AB { char a[10]; };\n" "void f(void) {\n" " struct AB ab;\n" " strcpy(ab.a, STR);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { char a[10]; };\n" "void f(void) {\n" " struct AB ab;\n" " strcpy(x, ab.a);\n" "}\n", "test.c"); TODO_ASSERT_EQUALS("error", "", errout.str()); checkUninitVar("struct AB { int a; };\n" "void f(void) {\n" " struct AB ab;\n" " dosomething(ab.a);\n" "}\n", "test.c"); TODO_ASSERT_EQUALS("error","", errout.str()); } checkUninitVar("struct AB { int a; int b; };\n" "void do_something(const struct AB ab);\n" "void f(void) {\n" " struct AB ab;\n" " ab = getAB();\n" " do_something(ab);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); { // #6769 - calling method that might assign struct members checkUninitVar("struct AB { int a; int b; void set(); };\n" "void f(void) {\n" " struct AB ab;\n" " ab.set();\n" " x = ab;\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { int a; int get() const; };\n" "void f(void) {\n" " struct AB ab;\n" " ab.get();\n" " x = ab;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; void dostuff() {} };\n" "void f(void) {\n" " struct AB ab;\n" " ab.dostuff();\n" " x = ab;\n" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); } checkUninitVar("struct AB { int a; struct { int b; int c; } s; };\n" "void do_something(const struct AB ab);\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 1;\n" " ab.s.b = 2;\n" " ab.s.c = 3;\n" " do_something(ab);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct conf {\n" " char x;\n" "};\n" "\n" "void do_something(struct conf ant_conf);\n" "\n" "void f(void) {\n" " struct conf c;\n" " initdata(&c);\n" " do_something(c);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct PIXEL {\n" " union {\n" " struct { unsigned char red,green,blue,alpha; };\n" " unsigned int color;\n" " };\n" "};\n" "\n" "unsigned char f() {\n" " struct PIXEL p1;\n" " p1.color = 255;\n" " return p1.red;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "int f() {\n" " struct AB *ab;\n" " for (i = 1; i < 10; i++) {\n" " if (condition && (ab = getab()) != NULL) {\n" " a = ab->a;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "int f(int x) {\n" " struct AB *ab;\n" " if (x == 0) {\n" " ab = getab();\n" " }\n" " if (x == 0 && (ab != NULL || ab->a == 0)) { }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct A { int *x; };\n" // declarationId is 0 for "delete" "void foo(void *info, void*p);\n" "void bar(void) {\n" " struct A *delete = 0;\n" " foo( info, NULL );\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct ABC { int a; int b; int c; };\n" "void foo(int x, const struct ABC *abc);\n" "void bar(void) {\n" " struct ABC abc;\n" " foo(123, &abc);\n" " return abc.b;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized struct member: abc.a\n" "[test.cpp:5]: (error) Uninitialized struct member: abc.b\n" "[test.cpp:6]: (error) Uninitialized struct member: abc.b\n" "[test.cpp:5]: (error) Uninitialized struct member: abc.c\n", errout.str()); // return checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 0;\n" " return ab.b;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:5]: (error) Uninitialized struct member: ab.b\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " ab.a = 0;\n" " return ab.a;\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); // checkIfForWhileHead checkUninitVar("struct FRED {\n" " int a;\n" " int b;\n" "};\n" "\n" "void f(void) {\n" " struct FRED fred;\n" " fred.a = do_something();\n" " if (fred.a == 0) { }\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct FRED {\n" " int a;\n" " int b;\n" "};\n" "\n" "void f(void) {\n" " struct FRED fred;\n" " fred.a = do_something();\n" " if (fred.b == 0) { }\n" "}\n", "test.c", false); ASSERT_EQUALS("[test.c:9]: (error) Uninitialized struct member: fred.b\n", errout.str()); checkUninitVar("struct Fred { int a; };\n" "void f() {\n" " struct Fred fred;\n" " if (fred.a==1) {}\n" "}", "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Uninitialized struct member: fred.a\n", errout.str()); checkUninitVar("struct S { int n; int m; };\n" "void f(void) {\n" " struct S s;\n" " for (s.n = 0; s.n <= 10; s.n++) { }\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void test2() {\n" " struct { char type; } s_d;\n" " if (foo(&s_d.type)){}\n" "}"); ASSERT_EQUALS("", errout.str()); // for checkUninitVar("struct AB { int a; };\n" "void f() {\n" " struct AB ab;\n" " while (x) { clear(ab); z = ab.a; }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("struct AB { int a; };\n" "void f() {\n" " struct AB ab;\n" " while (x) { ab.a = ab.a + 1; }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; };\n" "void f() {\n" " struct AB ab;\n" " while (x) { init(&ab); z = ab.a; }\n" "}"); ASSERT_EQUALS("", errout.str()); // address of member checkUninitVar("struct AB { int a[10]; int b; };\n" "void f() {\n" " struct AB ab;\n" " int *p = ab.a;\n" "}"); ASSERT_EQUALS("", errout.str()); // non static data-member initialization checkUninitVar("struct AB { int a=1; int b; };\n" "void f(void) {\n" " struct AB ab;\n" " int a = ab.a;\n" " int b = ab.b;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized struct member: ab.b\n", errout.str()); // STL class member checkUninitVar("struct A {\n" " std::map m;\n" " int i;\n" "};\n" "void foo() {\n" " A a;\n" " x = a.m;\n" "}"); ASSERT_EQUALS("", errout.str()); // Unknown type (C++) checkUninitVar("struct A {\n" " C m;\n" " int i;\n" "};\n" "void foo() {\n" " A a;\n" " x = a.m;\n" "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); // Unknown type (C) checkUninitVar("struct A {\n" " C m;\n" " int i;\n" "};\n" "void foo() {\n" " A a;\n" " x = a.m;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:7]: (error) Uninitialized struct member: a.m\n", errout.str()); // Type with constructor checkUninitVar("class C { C(); }\n" "struct A {\n" " C m;\n" " int i;\n" "};\n" "void foo() {\n" " A a;\n" " x = a.m;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar2_while() { // for, while checkUninitVar("void f() {\n" " int x;\n" " while (a) {\n" " x = x + 1;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " do {\n" " x = x + 1;\n" " } while (a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " for (int x = x; x < 10; x++) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void f() {\n" " for (Element *ptr3 = ptr3->Next(); ptr3; ptr3 = ptr3->Next()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: ptr3\n", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " while (a) {\n" " init(&x);\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " while (a) {\n" " if (b) x++;\n" " else x = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " for (int i = 0; i < 10; i += x) {\n" " x = y;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int x;\n" " for (int i = 0; i < 10; i += x) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("int f() {\n" " int i;\n" " for (i=0;i<9;++i)\n" " if (foo()) return i;\n" " return 9;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " int i;\n" " do {} while (!getvalue(&i));\n" " i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(void) {\n" " int x;\n" " while (a()) {\n" // <- condition must always be true or there will be problem " if (b()) {\n" " x = 1;\n" " break;" " }\n" " }\n" " return x;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); checkUninitVar("int f(void) {\n" " int x;\n" " while (a()) {\n" " if (b() && (x=1)) {\n" " return x;\n" " }\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(void) {\n" " int x;\n" " for (;;) {\n" " int a = x+1;\n" " do_something(a);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("struct AB {int a; int b;};\n" "void f(void) {\n" " struct AB ab;\n" " while (true) {\n" " int a = 1+ab.a;\n" " do_something(a);\n" " }\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:5]: (error) Uninitialized variable: ab\n" "[test.c:5]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("void f(int i) {\n" // #4569 fp " float *buffer;\n" " if(i>10) buffer = f;\n" " if(i>10) {\n" " for (int i=0;i<10;i++)\n" " buffer[i] = 0;\n" // <- fp " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(){\n" // #4519 - fp: inline assembler in loop " int x;\n" " for (int i = 0; i < 10; i++) {\n" " asm(\"foo\");\n" " if (x & 0xf1) { }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static void f(void) {\n" " struct ABC *abc;\n" " for (i = 0; i < 10; i++)\n" " x += sizeof(*abc);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(void) {\n" // #4879 " int i;\n" " while (x) {\n" " for (i = 0; i < 5; i++)\n" " a[i] = b[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(void) {\n" // #5658 " struct Foo *foo;\n" " while (true) {\n" " foo = malloc(sizeof(*foo));\n" " foo->x = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(void) {\n" " int i;\n" " while (x) {\n" " for (i=0,y=i;;){}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f() {\n" " char *p = (char *)malloc(256);\n" " while(*p && *p == '_')\n" " p++;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory is allocated but not initialized: p\n", errout.str()); // #6646 - init in for loop checkUninitVar("void f() {\n" // No FP " for (int i;;i++)\n" " dostuff(&i);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // No FN " for (int i;;i++)\n" " a=i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: i\n", errout.str()); } void uninitvar2_4494() { checkUninitVar("namespace N1 {\n" " class Fred {\n" " public:\n" " static void f1(char *p) { *p = 0; }\n" " };\n" " void fa(void) { char *p; Fred::f1(p); }\n" " void fb(void) { char *p; Fred::f2(p); }\n" " void fc(void) { char *p; ::N1::Fred::f1(p); }\n" " void fd(void) { char *p; ::N1::Fred::f2(p); }\n" "}\n" "namespace N2 {\n" " static void f1(char *p) { *p = 0; }\n" " void fa(void) { char *p; f1(p); }\n" " void fb(void) { char *p; f2(p); }\n" " void fc(void) { char *p; N1::Fred::f1(p); }\n" " void fd(void) { char *p; N1::Fred::f2(p); }\n" " void fe(void) { char *p; ::N1::Fred::f1(p); }\n" " void ff(void) { char *p; ::N1::Fred::f2(p); }\n" " void fg(void) { char *p; Foo::f1(p); }\n" " void fh(void) { char *p; Foo::f2(p); }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: p\n" "[test.cpp:8]: (error) Uninitialized variable: p\n" "[test.cpp:13]: (error) Uninitialized variable: p\n" "[test.cpp:15]: (error) Uninitialized variable: p\n" "[test.cpp:17]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("class Fred {\n" "public:\n" " void f1(char *p) { *p = 0; }\n" "};\n" "Fred fred;\n" "void f(void) {\n" " char *p;\n" " fred.f1(p);\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("class Fred {\n" "public:\n" " class Wilma {\n" " public:\n" " class Barney {\n" " public:\n" " class Betty {\n" " public:\n" " void f1(char *p) { *p = 0; }\n" " };\n" " Betty betty;\n" " };\n" " Barney barney;\n" " };\n" " Wilma wilma;\n" "};\n" "Fred fred;\n" "void f(void) {\n" " char *p;\n" " fred.wilma.barney.betty.f1(p);\n" "}"); ASSERT_EQUALS("[test.cpp:20]: (error) Uninitialized variable: p\n", errout.str()); } void uninitvar2_malloc() { checkUninitVar("int f() {\n" " int *p = malloc(40);\n" " return *p;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("int f() {\n" " int *p = malloc(40);\n" " var = *p;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "int f() {\n" " struct AB *ab = malloc(sizeof(struct AB));\n" " return ab->a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: ab\n" "[test.cpp:4]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct t_udf_file { int dir_left; };\n" "\n" "void f() {\n" " struct t_udf_file *newf;\n" " newf = malloc(sizeof(*newf));\n" " if (!newf) return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char *s = malloc(100);\n" " if (s != NULL) { }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char *p = malloc(100);\n" " p || assert_failed();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " char *p = malloc(100);\n" " x = p;\n" "}"); ASSERT_EQUALS("", errout.str()); // function parameter (treat it as initialized until malloc is used) checkUninitVar("int f(int *p) {\n" " if (*p == 1) {}\n" // no error " p = malloc(256);\n" " return *p;\n" // error "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "int f(struct AB *ab) {\n" " if (ab->a == 1) {}\n" // no error " ab = malloc(sizeof(struct AB));\n" " return ab->a;\n" // error "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar("struct AB { int a; int b; };\n" "void do_something(struct AB *ab);\n" // unknown function "int f() {\n" " struct AB *ab = malloc(sizeof(struct AB));\n" " do_something(ab);\n" "}"); ASSERT_EQUALS("", errout.str()); // analysis failed. varid 0. checkUninitVar("void *vlc_custom_create (vlc_object_t *parent, size_t length, const char *typename) {\n" " assert (length >= sizeof (vlc_object_t));\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar_ternaryexpression() { // #4683 checkUninitVar("struct B { int asd; };\n" "int f() {\n" " int a=0;\n" " struct B *b;\n" " if (x) {\n" " a = 1;\n" " b = p;\n" " }\n" " return a ? b->asd : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar_rangeBasedFor() { // #7078 checkUninitVar("void function(Entry& entry) {\n" " for (auto* expr : entry.exprs) {\n" " expr->operate();\n" " expr->operate();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void trac_4871() { // #4871 checkUninitVar("void pickup(int a) {\n" "bool using_planner_action;\n" "if (a) {\n" " using_planner_action = false;\n" "}\n" "else {\n" " try\n" " {}\n" " catch (std::exception &ex) {\n" " return;\n" " }\n" " using_planner_action = true;\n" "}\n" "if (using_planner_action) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void syntax_error() { // Ticket #5073 const char code[] = "struct flex_array {};\n" "struct cgroup_taskset {};\n" "void cgroup_attach_task() {\n" " struct flex_array *group;\n" " struct cgroup_taskset tset = { };\n" " do { } while_each_thread(leader, tsk);\n" "}"; ASSERT_THROW(checkUninitVar(code), InternalError); } void trac_5970() { // Ticket #5970 checkUninitVar("void DES_ede3_ofb64_encrypt() {\n" " DES_cblock d; \n" " char *dp; \n" " dp=(char *)d; \n" " init(dp); \n" "}", "test.c"); TODO_ASSERT_EQUALS("", "[test.c:4]: (error) Uninitialized variable: d\n", errout.str()); } void checkDeadPointer(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 code.. CheckUninitVar check(&tokenizer, &settings, this); check.deadPointer(); } void valueFlowUninit(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. settings.debugwarnings = false; settings.experimental = false; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check for redundant code.. CheckUninitVar checkuninitvar(&tokenizer, &settings, this); checkuninitvar.valueFlowUninit(); } void valueFlowUninit() { valueFlowUninit("void f() {\n" " int x;\n" " switch (x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); valueFlowUninit("int f() {\n" " int x;\n" " init(x);\n" " return x;\n" // TODO: inconclusive ? "}"); ASSERT_EQUALS("", errout.str()); valueFlowUninit("void f() {\n" // #8172 " char **x;\n" " if (2 < sizeof(*x)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void isVariableUsageDeref() { // *p checkUninitVar("void f() {\n" " char a[10];\n" " char c = *a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" " char a[SIZE+10];\n" " char c = *a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" " char a[10];\n" " *a += 10;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("void f() {\n" " int a[10][10];\n" " dostuff(*a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void deadPointer() { checkDeadPointer("void f() {\n" " int *p = p1;\n" " if (cond) {\n" " int x;\n" " p = &x;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Dead pointer usage. Pointer 'p' is dead if it has been assigned '&x' at line 5.\n", errout.str()); // FP: don't warn in subfunction checkDeadPointer("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) checkDeadPointer("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()); checkDeadPointer("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:7]: (error) Dead pointer usage. Pointer 'former_hover' is dead if it has been assigned '&item' at line 5.\n", errout.str()); // #6575 checkDeadPointer("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()); } }; REGISTER_TEST(TestUninitVar) cppcheck-1.82/test/testunusedfunctions.cpp000066400000000000000000000273171322667425100210650ustar00rootroot00000000000000/* * 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 "checkunusedfunctions.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include class TestUnusedFunctions : public TestFixture { public: TestUnusedFunctions() : TestFixture("TestUnusedFunctions") { } private: Settings settings; void run() { settings.addEnabled("style"); TEST_CASE(incondition); TEST_CASE(return1); TEST_CASE(return2); TEST_CASE(callback1); TEST_CASE(else1); TEST_CASE(functionpointer); TEST_CASE(template1); TEST_CASE(template2); TEST_CASE(template3); TEST_CASE(throwIsNotAFunction); TEST_CASE(unusedError); TEST_CASE(unusedMain); TEST_CASE(initializationIsNotAFunction); TEST_CASE(operator1); // #3195 TEST_CASE(returnRef); TEST_CASE(attribute); // #3471 - FP __attribute__(constructor) TEST_CASE(initializer_list); TEST_CASE(member_function_ternary); TEST_CASE(boost); TEST_CASE(multipleFiles); // same function name in multiple files TEST_CASE(lineNumber); // Ticket 3059 TEST_CASE(ignore_declaration); // ignore declaration } void check(const char code[], Settings::PlatformType platform = Settings::Native) { // Clear the error buffer.. errout.str(""); settings.platform(platform); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for unused functions.. CheckUnusedFunctions checkUnusedFunctions(&tokenizer, &settings, this); checkUnusedFunctions.parseTokens(tokenizer, "someFile.c", &settings); // check() returns error if and only if errout is not empty. if (checkUnusedFunctions.check(this, settings)) ASSERT(errout.str() != ""); else ASSERT_EQUALS("", errout.str()); } void incondition() { check("int f1()\n" "{\n" " if (f1())\n" " { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void return1() { check("int f1()\n" "{\n" " return f1();\n" "}"); ASSERT_EQUALS("", errout.str()); } void return2() { check("char * foo()\n" "{\n" " return *foo();\n" "}"); ASSERT_EQUALS("", errout.str()); } void callback1() { check("void f1()\n" "{\n" " void (*f)() = cond ? f1 : NULL;\n" "}"); ASSERT_EQUALS("", errout.str()); } void else1() { check("void f1()\n" "{\n" " if (cond) ;\n" " else f1();\n" "}"); ASSERT_EQUALS("", errout.str()); } void functionpointer() { check("void foo() { }\n" "int main() {\n" " f(&foo);\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() { }\n" "int main() {\n" " f(&::foo);\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace abc {\n" " void foo() { }\n" "};\n" "int main() {\n" " f(&abc::foo);\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace abc {\n" " void foo() { }\n" "};\n" "int main() {\n" " f = &abc::foo;\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace abc {\n" " void foo() { }\n" "};\n" "int main() {\n" " f = &::abc::foo;\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace abc {\n" // #3875 " void foo() { }\n" "};\n" "int main() {\n" " f(abc::foo);\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); } void template1() { check("template void foo() { }\n" "\n" "int main()\n" "{\n" " foo();\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); } void template2() { check("void f() { }\n" "\n" "template void g()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("", errout.str()); } void template3() { // #4701 check("class X {\n" "public:\n" " void bar() { foo(0); }\n" "private:\n" " template void foo( T t ) const;\n" "};\n" "template void X::foo( T t ) const { }\n"); ASSERT_EQUALS("[test.cpp:3]: (style) The function 'bar' is never used.\n", errout.str()); } void throwIsNotAFunction() { check("struct A {void f() const throw () {}}; int main() {A a; a.f();}"); ASSERT_EQUALS("", errout.str()); } void unusedError() { check("void foo() {}\n" "int main()\n"); ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str()); check("void foo() const {}\n" "int main()\n"); ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str()); check("void foo() const throw() {}\n" "int main()\n"); ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str()); check("void foo() throw() {}\n" "int main()\n"); ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str()); } void unusedMain() { check("int main() { }"); ASSERT_EQUALS("", errout.str()); check("int _tmain() { }", Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("int WinMain() { }", Settings::Win32A); ASSERT_EQUALS("", errout.str()); } void initializationIsNotAFunction() { check("struct B: N::A {\n" " B(): N::A() {};\n" "};"); ASSERT_EQUALS("", errout.str()); } void operator1() { check("struct Foo { void operator()(int a) {} };"); ASSERT_EQUALS("", errout.str()); check("struct Foo { operator std::string(int a) {} };"); ASSERT_EQUALS("", errout.str()); } void returnRef() { check("int& foo() {return x;}"); ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str()); } void attribute() { // #3471 - FP __attribute__((constructor)) check("void __attribute__((constructor)) f() {}"); ASSERT_EQUALS("", errout.str()); check("void __attribute__((constructor(1000))) f() {}"); ASSERT_EQUALS("", errout.str()); check("void __attribute__((destructor)) f() {}"); ASSERT_EQUALS("", errout.str()); check("void __attribute__((destructor(1000))) f() {}"); ASSERT_EQUALS("", errout.str()); // alternate syntax check("__attribute__((constructor)) void f() {}"); ASSERT_EQUALS("", errout.str()); check("__attribute__((constructor(1000))) void f() {}"); ASSERT_EQUALS("", errout.str()); check("__attribute__((destructor)) void f() {}"); ASSERT_EQUALS("", errout.str()); check("__attribute__((destructor(1000))) void f() {}"); ASSERT_EQUALS("", errout.str()); // alternate syntax check("void f() __attribute__((constructor));\n" "void f() { }"); ASSERT_EQUALS("", errout.str()); check("void f() __attribute__((constructor(1000)));\n" "void f() { }"); ASSERT_EQUALS("", errout.str()); check("void f() __attribute__((destructor));\n" "void f() { }"); ASSERT_EQUALS("", errout.str()); check("void f() __attribute__((destructor(1000)));\n" "void f() { }"); ASSERT_EQUALS("", errout.str()); // Don't crash on wrong syntax check("int x __attribute__((constructor));\n" "int x __attribute__((destructor));"); ASSERT_EQUALS("", errout.str()); } void initializer_list() { check("int foo() { return 0; }\n" "struct A {\n" " A() : m_i(foo())\n" " {}\n" "int m_i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void member_function_ternary() { check("struct Foo {\n" " void F1() {}\n" " void F2() {}\n" "};\n" "int main(int argc, char *argv[]) {\n" " Foo foo;\n" " void (Foo::*ptr)();\n" " ptr = (argc > 1 && !strcmp(argv[1], \"F2\")) ? &Foo::F2 : &Foo::F1;\n" " (foo.*ptr)();\n" "}"); ASSERT_EQUALS("", errout.str()); } void boost() { check("static void _xy(const char *b, const char *e)\n" "{}\n" "parse(line, blanks_p >> ident[&_xy] >> blanks_p >> eol_p).full"); ASSERT_EQUALS("", errout.str()); } void multipleFiles() { Tokenizer tokenizer(&settings, this); CheckUnusedFunctions c(&tokenizer, &settings, nullptr); // Clear the error buffer.. errout.str(""); const char code[] = "static void f() { }"; for (int i = 1; i <= 2; ++i) { std::ostringstream fname; fname << "test" << i << ".cpp"; // Clear the error buffer.. errout.str(""); Tokenizer tokenizer2(&settings, this); std::istringstream istr(code); tokenizer2.tokenize(istr, fname.str().c_str()); c.parseTokens(tokenizer2, "someFile.c", &settings); } // Check for unused functions.. c.check(this, settings); ASSERT_EQUALS("[test1.cpp:1]: (style) The function 'f' is never used.\n", errout.str()); } void lineNumber() { check("void foo() {}\n" "void bar() {}\n" "int main()\n"); ASSERT_EQUALS("[test.cpp:2]: (style) The function 'bar' is never used.\n" "[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str()); } void ignore_declaration() { check("void f();\n" "void f() {}"); ASSERT_EQUALS("[test.cpp:2]: (style) The function 'f' is never used.\n", errout.str()); check("void f(void) {}\n" "void (*list[])(void) = {f}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestUnusedFunctions) cppcheck-1.82/test/testunusedprivfunc.cpp000066400000000000000000000560061322667425100207060ustar00rootroot00000000000000/* * 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 "checkclass.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include class TestUnusedPrivateFunction : public TestFixture { public: TestUnusedPrivateFunction() : TestFixture("TestUnusedPrivateFunction") { } private: Settings settings; void run() { settings.addEnabled("style"); TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); TEST_CASE(test4); TEST_CASE(test5); TEST_CASE(test6); // ticket #2602 // [ 2236547 ] False positive --style unused function, called via pointer TEST_CASE(func_pointer1); TEST_CASE(func_pointer2); TEST_CASE(func_pointer3); TEST_CASE(func_pointer4); // ticket #2807 TEST_CASE(func_pointer5); // ticket #2233 TEST_CASE(func_pointer6); // ticket #4787 TEST_CASE(ctor); TEST_CASE(ctor2); TEST_CASE(classInClass); TEST_CASE(sameFunctionNames); TEST_CASE(incompleteImplementation); TEST_CASE(derivedClass); // skip warning for derived classes. It might be a virtual function. TEST_CASE(friendClass); TEST_CASE(borland1); // skip FP when using __property TEST_CASE(borland2); // skip FP when using __published // No false positives when there are "unused" templates that are removed in the simplified token list TEST_CASE(template1); // #2407 - FP when called from operator() TEST_CASE(fp_operator); TEST_CASE(testDoesNotIdentifyMethodAsFirstFunctionArgument); // #2480 TEST_CASE(testDoesNotIdentifyMethodAsMiddleFunctionArgument); TEST_CASE(testDoesNotIdentifyMethodAsLastFunctionArgument); TEST_CASE(multiFile); TEST_CASE(unknownBaseTemplate); // ticket #2580 TEST_CASE(hierarchie_loop); // ticket 5590 TEST_CASE(staticVariable); //ticket #5566 } void check(const char code[], Settings::PlatformType platform = Settings::Native) { // Clear the error buffer.. errout.str(""); settings.platform(platform); // Raw tokens.. std::vector files; files.push_back("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(""); tokenizer.simplifyTokenList2(); // Check for unused private functions.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.privateFunctions(); } void test1() { check("class Fred\n" "{\n" "private:\n" " unsigned int f();\n" "public:\n" " Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{ }\n" "\n" "unsigned int Fred::f()\n" "{ }"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused private function: 'Fred::f'\n", errout.str()); check("#line 1 \"p.h\"\n" "class Fred\n" "{\n" "private:\n" " unsigned int f();\n" "public:\n" " Fred();\n" "};\n" "\n" "#line 1 \"p.cpp\"\n" "Fred::Fred()\n" "{ }\n" "\n" "unsigned int Fred::f()\n" "{ }"); ASSERT_EQUALS("[p.h:4]: (style) Unused private function: 'Fred::f'\n", errout.str()); check("#line 1 \"p.h\"\n" "class Fred\n" "{\n" "private:\n" "void f();\n" "};\n" "\n" "\n" "#line 1 \"p.cpp\"\n" "\n" "void Fred::f()\n" "{\n" "}"); ASSERT_EQUALS("[p.h:4]: (style) Unused private function: 'Fred::f'\n", errout.str()); // Don't warn about include files which implementation we don't see check("#line 1 \"p.h\"\n" "class Fred\n" "{\n" "private:\n" "void f();\n" "void g() {}\n" "};\n" "\n" "#line 1 \"p.cpp\"\n" "\n" "int main()\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); } void test2() { check("class A {\n" "public:\n" " A();\n" "\n" " void a() const\n" " { b(); }\n" "private:\n" " void b( ) const\n" " { }\n" "};\n" "\n" "A::A()\n" "{ }"); ASSERT_EQUALS("", errout.str()); } void test3() { check("class A {\n" "public:\n" " A() { }\n" " ~A();\n" "private:\n" " void B() { }\n" "};\n" "\n" "A::~A()\n" "{ B(); }"); ASSERT_EQUALS("", errout.str()); } void test4() { check("class A {\n" "public:\n" " A();\n" "private:\n" " bool _owner;\n" " void b() { }\n" "};\n" "\n" "A::A() : _owner(false)\n" "{ b(); }"); ASSERT_EQUALS("", errout.str()); } void test5() { check("class A {\n" "private:\n" " A() : lock(new Lock())\n" " { }\n" " Lock *lock;\n" "};"); ASSERT_EQUALS("", errout.str()); } void test6() { // ticket #2602 segmentation fault check("class A {\n" " A& operator=(const A&);\n" "};"); ASSERT_EQUALS("", errout.str()); } void func_pointer1() { check("class Fred\n" "{\n" "private:\n" " typedef void (*testfp)();\n" "\n" " testfp get()\n" " {\n" " return test;\n" " }\n" "\n" " static void test()\n" " { }\n" "\n" "public:\n" " Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{}"); ASSERT_EQUALS("[test.cpp:6]: (style) Unused private function: 'Fred::get'\n", errout.str()); } void func_pointer2() { check("class UnusedPrivateFunctionMemberPointer\n" "{\n" "public:\n" " UnusedPrivateFunctionMemberPointer()\n" " : mObserver(this, &UnusedPrivateFunctionMemberPointer::callback)\n" " {}\n" "\n" "private:\n" " void callback(const& unsigned) const {}\n" "\n" " Observer mObserver;\n" "};"); ASSERT_EQUALS("", errout.str()); } void func_pointer3() { check("class c1\n" "{\n" "public:\n" " c1()\n" " { sigc::mem_fun(this, &c1::f1); }\n" "\n" "private:\n" " void f1() const {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void func_pointer4() { // ticket #2807 check("class myclass {\n" "public:\n" " myclass();\n" "private:\n" " static void f();\n" " void (*fptr)();\n" "};\n" "myclass::myclass() { fptr = &f; }\n" "void myclass::f() {}"); ASSERT_EQUALS("", errout.str()); } void func_pointer5() { check("class A {\n" "public:\n" " A() { f = A::func; }\n" " void (*f)();\n" "private:\n" " static void func() { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void func_pointer6() { // #4787 check("class Test {\n" "private:\n" " static void a(const char* p) { }\n" "public:\n" " void test(void* f = a) {\n" " f(\"test\");\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void ctor() { check("class PrivateCtor\n" "{\n" "private:\n" " PrivateCtor(int threadNum) :\n" " numOfThreads(threadNum)\n" " {\n" " }\n" "\n" " int numOfThreads;\n" "};"); ASSERT_EQUALS("", errout.str()); } void ctor2() { check("struct State {\n" " State(double const totalWeighting= TotalWeighting()) :\n" " totalWeighting_(totalWeighting) {}\n" "private:\n" " double TotalWeighting() { return 123.0; }\n" // called from constructor "public:\n" " double totalWeighting_;\n" "};"); ASSERT_EQUALS("", errout.str()); } void classInClass() { check("class A\n" "{\n" "public:\n" "\n" " class B\n" " {\n" " private:\n" " };\n" "private:\n" " static void f()\n" " { }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (style) Unused private function: 'A::f'\n", errout.str()); check("class A\n" "{\n" "public:\n" " A()\n" " { }\n" "\n" "private:\n" " void f()\n" " { }\n" "\n" " class B\n" " {\n" " public:\n" " B(A *a)\n" " { a->f(); }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" // #6968 - outer definition "public:\n" " class B;\n" "private:\n" " void f() {}\n" "}\n" "class A::B {" " B() { A a; a.f(); }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void sameFunctionNames() { check("class A\n" "{\n" "public:\n" " void a()\n" " {\n" " f(1);\n" " }\n" "\n" "private:\n" " void f() { }\n" " void f(int) { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void incompleteImplementation() { // The implementation for "A::a" is missing - so don't check if // "A::b" is used or not check("#file \"test.h\"\n" "class A\n" "{\n" "public:\n" " A();\n" " void a();\n" "private:\n" " void b();\n" "};\n" "#endfile\n" "A::A() { }\n" "void A::b() { }"); ASSERT_EQUALS("", errout.str()); } void derivedClass() { // skip warning in derived classes in case the base class is invisible check("class derived : public base\n" "{\n" "public:\n" " derived() : base() { }\n" "private:\n" " void f();\n" "};"); ASSERT_EQUALS("", errout.str()); check("class base {\n" "public:\n" " virtual void foo();\n" " void bar();\n" "};\n" "class derived : public base {\n" "private:\n" " void foo() {}\n" // Skip for overrides of virtual functions of base " void bar() {}\n" // Don't skip if no function is overridden "};"); ASSERT_EQUALS("[test.cpp:9]: (style) Unused private function: 'derived::bar'\n", errout.str()); check("class Base {\n" "private:\n" " virtual void func() = 0;\n" "public:\n" " void public_func() {\n" " func();\n" " };\n" "};\n" "\n" "class Derived1: public Base {\n" "private:\n" " void func() {}\n" "};\n" "class Derived2: public Derived1 {\n" "private:\n" " void func() {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Base {\n" "public:\n" " void dostuff() {\n" " f();\n" " }\n" "\n" "private:\n" " virtual Base* f() = 0;\n" "};\n" "\n" "class Derived : public Base {\n" "private:\n" " Derived* f() {\n" " return 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void friendClass() { // ticket #2459 - friend class check("class Foo {\n" "private:\n" " friend Bar;\n" // Unknown friend class " void f() { }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Bar {\n" " void g() { f(); }\n" // Friend class seen, but f not seen "};\n" "class Foo {\n" "private:\n" " friend Bar;\n" " void f();\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Bar {\n" " void g() { f(); }\n" // Friend class seen, but f() used in it "};\n" "class Foo {\n" "private:\n" " friend Bar;\n" " void f() { }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Bar {\n" // Friend class seen, f() not used in it "};\n" "class Foo {\n" " friend Bar;\n" " void f() { }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style) Unused private function: 'Foo::f'\n", errout.str()); } void borland1() { // ticket #2034 - Borland C++ __property check("class Foo {\n" "private:\n" " int getx() {\n" " return 123;\n" " }\n" "public:\n" " Foo() { }\n" " __property int x = {read=getx}\n" "};", Settings::Win32A); ASSERT_EQUALS("", errout.str()); } void borland2() { // ticket #3661 - Borland C++ __published check("class Foo {\n" "__published:\n" " int getx() {\n" " return 123;\n" " }\n" "public:\n" " Foo() { }\n" "};", Settings::Win32A); ASSERT_EQUALS("", errout.str()); } void template1() { // ticket #2067 - Template methods do not "use" private ones check("class A {\n" "public:\n" " template \n" " T getVal() const;\n" "\n" "private:\n" " int internalGetVal() const { return 8000; }\n" "};\n" "\n" "template \n" "T A::getVal() const {\n" " return internalGetVal();\n" "};"); ASSERT_EQUALS("", errout.str()); } void fp_operator() { // #2407 - FP when function is called from operator() check("class Fred\n" "{\n" "public:\n" " void operator()(int x) {\n" " startListening();\n" " }\n" "\n" "private:\n" " void startListening() {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " void operator()(int x) {\n" " }\n" "\n" "private:\n" " void startListening() {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (style) Unused private function: 'Fred::startListening'\n", errout.str()); // #5059 check("class Fred {\n" " void* operator new(size_t obj_size, size_t buf_size) {}\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:2]: (style) Unused private function: 'Fred::operatornew'\n", "", errout.str()); // No message for operators - we currently cannot check their usage check("class Fred {\n" " void* operator new(size_t obj_size, size_t buf_size) {}\n" "public:\n" " void* foo() { return new(size) Fred(); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void testDoesNotIdentifyMethodAsFirstFunctionArgument() { check("void callback(void (*func)(int), int arg)" "{" " (*func)(arg);" "}" "class MountOperation" "{" " static void Completed(int i);" "public:" " MountOperation(int i);" "};" "void MountOperation::Completed(int i)" "{" " std::cerr << i << std::endl;" "}" "MountOperation::MountOperation(int i)" "{" " callback(MountOperation::Completed, i);" "}" "int main(void)" "{" " MountOperation aExample(10);" "}" ); ASSERT_EQUALS("", errout.str()); } void testDoesNotIdentifyMethodAsMiddleFunctionArgument() { check("void callback(char, void (*func)(int), int arg)" "{" " (*func)(arg);" "}" "class MountOperation" "{" " static void Completed(int i);" "public:" " MountOperation(int i);" "};" "void MountOperation::Completed(int i)" "{" " std::cerr << i << std::endl;" "}" "MountOperation::MountOperation(int i)" "{" " callback('a', MountOperation::Completed, i);" "}" "int main(void)" "{" " MountOperation aExample(10);" "}" ); ASSERT_EQUALS("", errout.str()); } void testDoesNotIdentifyMethodAsLastFunctionArgument() { check("void callback(int arg, void (*func)(int))" "{" " (*func)(arg);" "}" "class MountOperation" "{" " static void Completed(int i);" "public:" " MountOperation(int i);" "};" "void MountOperation::Completed(int i)" "{" " std::cerr << i << std::endl;" "}" "MountOperation::MountOperation(int i)" "{" " callback(i, MountOperation::Completed);" "}" "int main(void)" "{" " MountOperation aExample(10);" "}" ); ASSERT_EQUALS("", errout.str()); } void multiFile() { // ticket #2567 check("#file \"test.h\"\n" "struct Fred\n" "{\n" " Fred()\n" " {\n" " Init();\n" " }\n" "private:\n" " void Init();\n" "};\n" "#endfile\n" "void Fred::Init()\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); } void unknownBaseTemplate() { // ticket #2580 check("class Bla : public Base2 {\n" "public:\n" " Bla() {}\n" "private:\n" " virtual void F() const;\n" "};\n" "void Bla::F() const { }"); ASSERT_EQUALS("", errout.str()); } void hierarchie_loop() { check("class InfiniteB : InfiniteA {\n" " class D {\n" " };\n" "};\n" "namespace N {\n" " class InfiniteA : InfiniteB {\n" " };\n" "}\n" "class InfiniteA : InfiniteB {\n" " void foo();\n" "};\n" "void InfiniteA::foo() {\n" " C a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void staticVariable() { check("class Foo {\n" " static int i;\n" " static int F() const { return 1; }\n" "};\n" "int Foo::i = Foo::F();"); ASSERT_EQUALS("", errout.str()); check("class Foo {\n" " static int i;\n" " int F() const { return 1; }\n" "};\n" "Foo f;\n" "int Foo::i = f.F();"); ASSERT_EQUALS("", errout.str()); check("class Foo {\n" " static int i;\n" " static int F() const { return 1; }\n" "};\n" "int Foo::i = sth();" "int i = F();"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused private function: 'Foo::F'\n", errout.str()); } }; REGISTER_TEST(TestUnusedPrivateFunction) cppcheck-1.82/test/testunusedvar.cpp000066400000000000000000005433011322667425100176410ustar00rootroot00000000000000/* * 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 "checkunusedvar.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestUnusedVar : public TestFixture { public: TestUnusedVar() : TestFixture("TestUnusedVar") { } private: Settings settings; void run() { settings.addEnabled("style"); TEST_CASE(emptyclass); // #5355 - False positive: Variable is not assigned a value. TEST_CASE(emptystruct); // #5355 - False positive: Variable is not assigned a value. TEST_CASE(structmember1); TEST_CASE(structmember2); TEST_CASE(structmember3); TEST_CASE(structmember4); TEST_CASE(structmember5); TEST_CASE(structmember6); TEST_CASE(structmember7); TEST_CASE(structmember8); TEST_CASE(structmember9); // #2017 - struct is inherited TEST_CASE(structmember_extern); // No false positives for extern structs TEST_CASE(structmember10); TEST_CASE(structmember11); // #4168 - initialization with {} / passed by address to unknown function TEST_CASE(structmember12); // #7179 - FP unused structmember TEST_CASE(structmember13); // #3088 - __attribute__((packed)) TEST_CASE(structmember14); // #6508 - (struct x){1,2,..} TEST_CASE(structmember_sizeof); TEST_CASE(localvar1); TEST_CASE(localvar2); TEST_CASE(localvar3); TEST_CASE(localvar4); TEST_CASE(localvar5); TEST_CASE(localvar6); TEST_CASE(localvar7); TEST_CASE(localvar8); TEST_CASE(localvar9); // ticket #1605 TEST_CASE(localvar10); TEST_CASE(localvar11); TEST_CASE(localvar12); TEST_CASE(localvar13); // ticket #1640 TEST_CASE(localvar14); // ticket #5 TEST_CASE(localvar15); TEST_CASE(localvar16); // ticket #1709 TEST_CASE(localvar17); // ticket #1720 TEST_CASE(localvar18); // ticket #1723 TEST_CASE(localvar19); // ticket #1776 TEST_CASE(localvar20); // ticket #1799 TEST_CASE(localvar21); // ticket #1807 TEST_CASE(localvar22); // ticket #1811 TEST_CASE(localvar23); // ticket #1808 TEST_CASE(localvar24); // ticket #1803 TEST_CASE(localvar25); // ticket #1729 TEST_CASE(localvar26); // ticket #1894 TEST_CASE(localvar27); // ticket #2160 TEST_CASE(localvar28); // ticket #2205 TEST_CASE(localvar29); // ticket #2206 (array initialization) TEST_CASE(localvar30); TEST_CASE(localvar31); // ticket #2286 TEST_CASE(localvar32); // ticket #2330 TEST_CASE(localvar33); // ticket #2346 TEST_CASE(localvar34); // ticket #2368 TEST_CASE(localvar35); // ticket #2535 TEST_CASE(localvar36); // ticket #2805 TEST_CASE(localvar37); // ticket #3078 TEST_CASE(localvar38); TEST_CASE(localvar39); // ticket #3454 TEST_CASE(localvar40); // ticket #3473 TEST_CASE(localvar41); // ticket #3603 TEST_CASE(localvar42); // ticket #3742 TEST_CASE(localvar43); // ticket #3602 TEST_CASE(localvar44); // ticket #4020 TEST_CASE(localvar45); // ticket #4899 TEST_CASE(localvar46); // ticket #5491 (C++11 style initialization) TEST_CASE(localvar47); // ticket #6603 TEST_CASE(localvar48); // ticket #6954 TEST_CASE(localvar49); // ticket #7594 TEST_CASE(localvar50); // ticket #6261 : dostuff(cond ? buf1 : buf2) TEST_CASE(localvar51); // ticket #8128 - FN : tok = tok->next(); TEST_CASE(localvaralias1); TEST_CASE(localvaralias2); // ticket #1637 TEST_CASE(localvaralias3); // ticket #1639 TEST_CASE(localvaralias4); // ticket #1643 TEST_CASE(localvaralias5); // ticket #1647 TEST_CASE(localvaralias6); // ticket #1729 TEST_CASE(localvaralias7); // ticket #1732 TEST_CASE(localvaralias8); TEST_CASE(localvaralias9); // ticket #1996 TEST_CASE(localvaralias10); // ticket #2004 TEST_CASE(localvaralias11); // ticket #4423 - iterator TEST_CASE(localvaralias12); // ticket #4394 TEST_CASE(localvaralias13); // ticket #4487 TEST_CASE(localvaralias14); // ticket #5619 TEST_CASE(localvaralias15); // ticket #6315 TEST_CASE(localvarasm); TEST_CASE(localvarstatic); TEST_CASE(localvarextern); TEST_CASE(localvardynamic1); TEST_CASE(localvardynamic2); // ticket #2904 TEST_CASE(localvardynamic3); // ticket #3467 TEST_CASE(localvararray1); // ticket #2780 TEST_CASE(localvararray2); // ticket #3438 TEST_CASE(localvararray3); // ticket #3980 TEST_CASE(localvararray4); // ticket #4839 TEST_CASE(localvararray5); // ticket #7092 TEST_CASE(localvarstring1); TEST_CASE(localvarstring2); // ticket #2929 TEST_CASE(localvarconst1); TEST_CASE(localvarconst2); TEST_CASE(localvarthrow); // ticket #3687 TEST_CASE(localVarStd); // Don't give false positives for variables in structs/unions TEST_CASE(localvarStruct1); TEST_CASE(localvarStruct2); TEST_CASE(localvarStruct3); TEST_CASE(localvarStruct5); TEST_CASE(localvarStruct6); TEST_CASE(localvarStructArray); TEST_CASE(localvarOp); // Usage with arithmetic operators TEST_CASE(localvarInvert); // Usage with inverted variable TEST_CASE(localvarIf); // Usage in if TEST_CASE(localvarIfElse); // return tmp1 ? tmp2 : tmp3; TEST_CASE(localvarOpAssign); // a |= b; TEST_CASE(localvarFor); // for ( ; var; ) TEST_CASE(localvarForEach); // #4155 - BOOST_FOREACH, hlist_for_each, etc TEST_CASE(localvarShift1); // 1 >> var TEST_CASE(localvarShift3); // x << y TEST_CASE(localvarCast); TEST_CASE(localvarClass); TEST_CASE(localvarUnused); TEST_CASE(localvarFunction); // ticket #1799 TEST_CASE(localvarIfNOT); // #3104 - if ( NOT var ) TEST_CASE(localvarAnd); // #3672 TEST_CASE(localvarSwitch); // #3744 - false positive when localvar is used in switch TEST_CASE(localvarNULL); // #4203 - Setting NULL value is not redundant - it is safe TEST_CASE(localvarUnusedGoto); // #4447, #4558 goto TEST_CASE(localvarRangeBasedFor); // #7075 TEST_CASE(localvarAssignInWhile); TEST_CASE(localvarTemplate); // #4955 - variable is used as template parameter TEST_CASE(localvarFuncPtr); // #7194 TEST_CASE(localvarAddr); // #7477 TEST_CASE(localvarCppInitialization); TEST_CASE(localvarCpp11Initialization); TEST_CASE(chainedAssignment); // #5466 TEST_CASE(crash1); TEST_CASE(crash2); TEST_CASE(usingNamespace); // #4585 TEST_CASE(lambdaFunction); // #5078 TEST_CASE(namespaces); // #7557 TEST_CASE(bracesInitCpp11);// #7895 - "int var{123}" initialization } void checkStructMemberUsage(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 unused variables.. CheckUnusedVar checkUnusedVar(&tokenizer, &settings, this); checkUnusedVar.checkStructMemberUsage(); } // #5355 - False positive: Variable is not assigned a value. void emptyclass() { functionVariableUsage("class Carla {\n" "};\n" "class Fred : Carla {\n" "};\n" "void foo() {\n" " Fred fred;\n" " throw fred;\n" "}"); ASSERT_EQUALS("", errout.str()); } // #5355 - False positive: Variable is not assigned a value. void emptystruct() { functionVariableUsage("struct Fred {\n" "};\n" "void foo() {\n" " Fred fred;\n" " throw fred;\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember1() { checkStructMemberUsage("struct abc\n" "{\n" " int a;\n" " int b;\n" " int c;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) struct member 'abc::a' is never used.\n" "[test.cpp:4]: (style) struct member 'abc::b' is never used.\n" "[test.cpp:5]: (style) struct member 'abc::c' is never used.\n", errout.str()); checkStructMemberUsage("union abc\n" "{\n" " int a;\n" " int b;\n" " int c;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) union member 'abc::a' is never used.\n" "[test.cpp:4]: (style) union member 'abc::b' is never used.\n" "[test.cpp:5]: (style) union member 'abc::c' is never used.\n", errout.str()); } void structmember2() { checkStructMemberUsage("struct ABC\n" "{\n" " int a;\n" " int b;\n" " int c;\n" "};\n" "\n" "void foo()\n" "{\n" " struct ABC abc;\n" " int a = abc.a;\n" " int b = abc.b;\n" " int c = abc.c;\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember3() { checkStructMemberUsage("struct ABC\n" "{\n" " int a;\n" " int b;\n" " int c;\n" "};\n" "\n" "static struct ABC abc[] = { {1, 2, 3} };\n" "\n" "void foo()\n" "{\n" " int a = abc[0].a;\n" " int b = abc[0].b;\n" " int c = abc[0].c;\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember4() { checkStructMemberUsage("struct ABC\n" "{\n" " const int a;\n" "};\n" "\n" "void foo()\n" "{\n" " ABC abc;\n" " if (abc.a == 2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember5() { checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" " void reset()\n" " {\n" " a = 1;\n" " b = 2;\n" " }\n" "};\n" "\n" "void foo()\n" "{\n" " struct AB ab;\n" " ab.reset();\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember6() { checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" "};\n" "\n" "void foo(char *buf)\n" "{\n" " struct AB *ab = (struct AB *)&buf[10];\n" "}"); ASSERT_EQUALS("", errout.str()); checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" "};\n" "\n" "void foo(char *buf)\n" "{\n" " struct AB *ab = (AB *)&buf[10];\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember7() { checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" "};\n" "\n" "void foo(struct AB *ab)\n" "{\n" " ab->a = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" "};\n" "\n" "void foo(struct AB _shuge *ab)\n" "{\n" " ab->a = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember8() { checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" "};\n" "\n" "void foo(char *ab)\n" "{\n" " ((AB *)ab)->b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember9() { checkStructMemberUsage("struct base {\n" " int a;\n" "};\n" "\n" "struct derived : public base {" "}"); ASSERT_EQUALS("", errout.str()); } void structmember10() { // Fred may have some useful side-effects checkStructMemberUsage("struct abc {\n" " Fred fred;\n" "};"); ASSERT_EQUALS("", errout.str()); } void structmember11() { // #4168 checkStructMemberUsage("struct abc { int x; };\n" "struct abc s = {0};\n" "void f() { do_something(&s); }"); ASSERT_EQUALS("", errout.str()); checkStructMemberUsage("struct abc { int x; };\n" "struct abc s = {0};\n" "void f() { }"); TODO_ASSERT_EQUALS("abc::x is not used", "", errout.str()); } void structmember12() { // #7179 checkStructMemberUsage("#include \n" "struct\n" "{\n" " union\n" " {\n" " struct\n" " {\n" " int a;\n" " } struct1;\n" " };\n" "} var = {0};\n" "int main(int argc, char *argv[])\n" "{\n" " printf(\"var.struct1.a = %d\", var.struct1.a);\n" " return 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void structmember13() { // #3088 - struct members required by hardware checkStructMemberUsage("struct S {\n" " int x;\n" "} __attribute__((packed));"); ASSERT_EQUALS("", errout.str()); } void structmember14() { // #6508 checkStructMemberUsage("struct bstr { char *bstart; size_t len; };\n" "struct bstr bstr0(void) {\n" " return (struct bstr){\"hello\",6};\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember_extern() { // extern struct => no false positive checkStructMemberUsage("extern struct AB\n" "{\n" " int a;\n" " int b;\n" "} ab;\n" "\n" "void foo()\n" "{\n" " ab.b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // global linkage => no false positive checkStructMemberUsage("struct AB\n" "{\n" " int a;\n" " int b;\n" "} ab;\n" "\n" "void foo()\n" "{\n" " ab.b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // static linkage => error message checkStructMemberUsage("static struct AB\n" "{\n" " int a;\n" " int b;\n" "} ab;\n" "\n" "void foo()\n" "{\n" " ab.b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) struct member 'AB::a' is never used.\n", errout.str()); } void structmember_sizeof() { checkStructMemberUsage("struct Header {\n" " uint8_t message_type;\n" "}\n" "\n" "input.skip(sizeof(Header));"); ASSERT_EQUALS("", errout.str()); checkStructMemberUsage("struct Header {\n" " uint8_t message_type;\n" "}\n" "\n" "input.skip(sizeof(struct Header));"); ASSERT_EQUALS("", errout.str()); } void functionVariableUsage(const char code[], const char filename[]="test.cpp") { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (!tokenizer.tokenize(istr, filename)) return; // Check for unused variables.. CheckUnusedVar checkUnusedVar(&tokenizer, &settings, this); checkUnusedVar.checkFunctionVariableUsage(); } void localvar1() { functionVariableUsage("void foo()\n" "{\n" " int i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int i(0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); // if a is undefined then Cppcheck can't determine if "int i(a)" is a // * variable declaration // * function declaration functionVariableUsage("void foo()\n" "{\n" " int i(a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int j = 0;\n" " int i(j);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int j = 0;\n" " int & i = j;\n" " x(j);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int j = 0;\n" " const int & i = j;\n" " x(j);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int j = 0;\n" " int & i(j);\n" " x(j);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int j = 0;\n" " const int & i(j);\n" " x(j);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int * j = 0;\n" " int * i(j);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int * j = 0;\n" " const int * i(j);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " bool i = false;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " bool i = true;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char *i;\n" " i = fgets();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); // undefined variables are not reported because they may be classes with constructors functionVariableUsage("undefined foo()\n" "{\n" " undefined i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("undefined foo()\n" "{\n" " undefined i = 0;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int i = undefined;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int * i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " void * i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " const void * i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " struct S * i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " const struct S * i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " struct S & i = j;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " const struct S & i = j;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " undefined * i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int i = 0;\n" " int j = i;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'j' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int i[10] = { 0 };\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo(int n)\n" "{\n" " int i[n] = { 0 };\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char i[10] = \"123456789\";\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char *i = \"123456789\";\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int i = 0,code=10;\n" " for(i = 0; i < 10; i++) {\n" " std::cout<f();\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char * i;\n" " if (i);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char * i = 0;\n" " if (i);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char * i = new char[10];\n" " if (i);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char *i;\n" " f(i);\n" "}"); functionVariableUsage("int a;\n" "void foo()\n" "{\n" " return &a;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int *p = a;\n" " for (int i = 0; i < 10; i++)\n" " p[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int *p = &a[0];\n" " for (int i = 0; i < 10; i++)\n" " p[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int x;\n" " a[0] = 0;\n" " x = a[0];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'x' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a, b, c;\n" " a = b = c = f();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int * foo()\n" "{\n" " return &undefined[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar9() { // ticket #1605 functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; )\n" " a[i++] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); } void localvar10() { functionVariableUsage("void foo(int x)\n" "{\n" " int i;\n" " if (x) {\n" " int i;\n" " } else {\n" " int i;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n" "[test.cpp:5]: (style) Unused variable: i\n" "[test.cpp:7]: (style) Unused variable: i\n", errout.str()); functionVariableUsage("void foo(int x)\n" "{\n" " int i;\n" " if (x)\n" " int i;\n" " else\n" " int i;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n" "[test.cpp:5]: (style) Unused variable: i\n" "[test.cpp:7]: (style) Unused variable: i\n", errout.str()); functionVariableUsage("void foo(int x)\n" "{\n" " int i;\n" " if (x) {\n" " int i;\n" " } else {\n" " int i = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n" "[test.cpp:5]: (style) Unused variable: i\n" "[test.cpp:7]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo(int x)\n" "{\n" " int i;\n" " if (x) {\n" " int i;\n" " } else {\n" " int i;\n" " }\n" " i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (style) Variable 'i' is assigned a value that is never used.\n" "[test.cpp:5]: (style) Unused variable: i\n" "[test.cpp:7]: (style) Unused variable: i\n", errout.str()); } void localvar11() { functionVariableUsage("void foo(int x)\n" "{\n" " int a = 0;\n" " if (x == 1)\n" " {\n" " a = 123;\n" // redundant assignment " return;\n" " }\n" " x = a;\n" "}"); ASSERT_EQUALS("", errout.str()); // The variable 'a' is initialized. But the initialized value is // never used. It is only initialized for security reasons. functionVariableUsage("void foo(int x)\n" "{\n" " int a = 0;\n" " if (x == 1)\n" " a = 123;\n" " else if (x == 2)\n" " a = 456;\n" " else\n" " return;\n" " x = a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar12() { // ticket #1574 functionVariableUsage("void foo()\n" "{\n" " int a, b, c, d, e, f;\n" " a = b = c = d = e = f = 0;\n" "\n" "}"); ASSERT_EQUALS( "[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'c' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'd' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'e' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'f' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a, b, c = 0;\n" " a = b = c;\n" "\n" "}"); TODO_ASSERT_EQUALS( "[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n" "[test.cpp:3]: (style) Variable 'c' is assigned a value that is never used.\n", "[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n" "[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); } void localvar13() { // ticket #1640 functionVariableUsage("void foo( OBJECT *obj )\n" "{\n" " int x;\n" " x = obj->ySize / 8;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'x' is assigned a value that is never used.\n", errout.str()); } void localvar14() { // ticket #5 functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n", errout.str()); } void localvar15() { functionVariableUsage("int foo()\n" "{\n" " int a = 5;\n" " int b[a];\n" " b[0] = 0;\n" " return b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo()\n" "{\n" " int a = 5;\n" " int * b[a];\n" " b[0] = &c;\n" " return *b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int * foo()\n" "{\n" " int a = 5;\n" " const int * b[a];\n" " b[0] = &c;\n" " return b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct B * foo()\n" "{\n" " int a = 5;\n" " struct B * b[a];\n" " b[0] = &c;\n" " return b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("const struct B * foo()\n" "{\n" " int a = 5;\n" " const struct B * b[a];\n" " b[0] = &c;\n" " return b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar16() { // ticket #1709 functionVariableUsage("int foo()\n" "{\n" " char buf[5];\n" " char *ptr = buf;\n" " *(ptr++) = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'buf' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int foo()\n" "{\n" " char buf[5];\n" " char *ptr = buf - 1;\n" " *(++ptr) = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'buf' is not assigned a value.\n", errout.str()); // #3910 functionVariableUsage("int foo() {\n" " char buf[5];\n" " char *data[2];\n" " data[0] = buf;\n" " do_something(data);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" " char buf1[5];\n" " char buf2[5];\n" " char *data[2];\n" " data[0] = buf1;\n" " data[1] = buf2;\n" " do_something(data);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar17() { // ticket #1720 // Don't crash when checking the code below! functionVariableUsage("void foo()\n" "{\n" " struct DATA *data;\n" " char *k = data->req;\n" " char *ptr;\n" " char *line_start;\n" " ptr = data->buffer;\n" " line_start = ptr;\n" " data->info = k;\n" " line_start = ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (style) Variable 'line_start' is assigned a value that is never used.\n", errout.str()); } void localvar18() { // ticket #1723 functionVariableUsage("A::A(int iValue) {\n" " UserDefinedException* pe = new UserDefinedException();\n" " throw pe;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar19() { // ticket #1776 functionVariableUsage("void foo() {\n" " int a[10];\n" " int c;\n" " c = *(a);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'a' is not assigned a value.\n" "[test.cpp:4]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); } void localvar20() { // ticket #1799 functionVariableUsage("void foo()\n" "{\n" " char c1 = 'c';\n" " char c2[] = { c1 };\n" " a(c2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar21() { // ticket #1807 functionVariableUsage("void foo()\n" "{\n" " char buffer[1024];\n" " bar((void *)buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar22() { // ticket #1811 functionVariableUsage("int foo(int u, int v)\n" "{\n" " int h, i;\n" " h = 0 ? u : v;\n" " i = 1 ? u : v;\n" " return h + i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar23() { // ticket #1808 functionVariableUsage("int foo(int c)\n" "{\n" " int a;\n" " int b[10];\n" " a = b[c] = 0;\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); } void localvar24() { // ticket #1803 functionVariableUsage("class MyException\n" "{\n" " virtual void raise() const\n" " {\n" " throw *this;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar25() { // ticket #1729 functionVariableUsage("int main() {\n" " int ppos = 1;\n" " int pneg = 0;\n" " const char*edge = ppos? \" +\" : pneg ? \" -\" : \"\";\n" " printf(\"This should be a '+' -> %s\n\", edge);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar26() { // ticket #1894 functionVariableUsage("int main() {\n" " const Fred &fred = getfred();\n" " int *p = fred.x();\n" " *p = 0;" "}"); ASSERT_EQUALS("", errout.str()); } void localvar27() { // ticket #2160 functionVariableUsage("void f(struct s *ptr) {\n" " int param = 1;\n" " ptr->param = param++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar28() { // ticket #2205 functionVariableUsage("void f(char* buffer, int value) {\n" " char* pos = buffer;\n" " int size = value;\n" " *(int*)pos = size;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar29() { // ticket #2206 functionVariableUsage("void f() {\n" " float s_ranges[] = { 0, 256 };\n" " float* ranges[] = { s_ranges };\n" " cout << ranges[0][0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar30() { // ticket #2264 functionVariableUsage("void f() {\n" " Engine *engine = e;\n" " x->engine = engine->clone();\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar31() { // ticket #2286 functionVariableUsage("void f() {\n" " int x = 0;\n" " a.x = x - b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar32() { // ticket #2330 - fstream >> x functionVariableUsage("void f() {\n" " int x;\n" " fstream &f = getfile();\n" " f >> x;\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #4596 - if (c >>= x) {} functionVariableUsage("void f() {\n" " int x;\n" " C c;\n" // possibly some stream class " if (c >>= x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " int x;\n" " C c;\n" " if (c >>= x) {}\n" "}", "test.c"); ASSERT_EQUALS("[test.c:4]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void f(int c) {\n" " int x;\n" " if (c >> x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'x' is not assigned a value.\n", errout.str()); functionVariableUsage("void f() {\n" " int x, y;\n" " std::cin >> x >> y;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " int x, y;\n" " std::cin >> (x >> y);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'x' is not assigned a value.\n" "[test.cpp:2]: (style) Variable 'y' is not assigned a value.\n", errout.str()); } void localvar33() { // ticket #2345 functionVariableUsage("void f() {\n" " Abc* abc = getabc();\n" " while (0 != (abc = abc->next())) {\n" " ++nOldNum;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar34() { // ticket #2368 functionVariableUsage("void f() {\n" " int i = 0;\n" " if (false) {\n" " } else {\n" " j -= i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar35() { // ticket #2535 functionVariableUsage("void f() {\n" " int a, b;\n" " x(1,a,b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar36() { // ticket #2805 functionVariableUsage("int f() {\n" " int a, b;\n" " a = 2 * (b = 3);\n" " return a + b;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int f() {\n" // ticket #4318 " int a,b;\n" " x(a, b=2);\n" // <- if param2 is passed-by-reference then b might be used in x "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" // ticket #6147 " int a = 0;\n" " bar(a=a+2);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" // ticket #6147 " int a = 0;\n" " bar(a=2);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); functionVariableUsage("void bar(int);\n" "int foo() {\n" " int a = 0;\n" " bar(a=a+2);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void localvar37() { // ticket #3078 functionVariableUsage("void f() {\n" " int a = 2;\n" " ints.at(a) = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar38() { functionVariableUsage("std::string f() {\n" " const char code[] = \"foo\";\n" " const std::string s1(sizeof_(code));\n" " const std::string s2 = sizeof_(code);\n" " return(s1+s2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar39() { functionVariableUsage("void f() {\n" " int a = 1;\n" " foo(x*a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar40() { functionVariableUsage("int f() {\n" " int a = 1;\n" " return x & a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar41() { // #3603 - false positive 'x is assigned a value that is never used' functionVariableUsage("int f() {\n" " int x = 1;\n" " int y = FOO::VALUE * x;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar42() { // #3742 functionVariableUsage("float g_float = 1;\n" "extern void SomeTestFunc(float);\n" "void MyFuncError()\n" "{\n" " const float floatA = 2.2f;\n" " const float floatTot = g_float * floatA;\n" " SomeTestFunc(floatTot);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("float g_float = 1;\n" "extern void SomeTestFunc(float);\n" "void MyFuncNoError()\n" "{\n" " const float floatB = 2.2f;\n" " const float floatTot = floatB * g_float;\n" " SomeTestFunc(floatTot);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("float g_float = 1;\n" "extern void SomeTestFunc(float);\n" "void MyFuncNoError2()\n" "{\n" " const float floatC = 2.2f;\n" " float floatTot = g_float * floatC;\n" " SomeTestFunc(floatTot);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar43() { // ticket #3602 (false positive) functionVariableUsage("void bar()\n" "{\n" " int * piArray = NULL;\n" " unsigned int uiArrayLength = 2048;\n" " unsigned int uiIndex;\n" "\n" " try\n" " {\n" " piArray = new int[uiArrayLength];\n" // Allocate memory " }\n" " catch (...)\n" " {\n" " SOME_MACRO\n" " delete [] piArray;\n" " return;\n" " }\n" " for (uiIndex = 0; uiIndex < uiArrayLength; uiIndex++)\n" " {\n" " piArray[uiIndex] = -1234;\n" " }\n" " delete [] piArray;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar44() { // #4020 - FP functionVariableUsage("void func() {\n" " int *sp_mem[2] = { 0x00, 0x00 };\n" " int src = 1, dst = 2;\n" " sp_mem[(dst + i)][3] = src;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar45() { // #4899 - FP functionVariableUsage("int func() {\n" " int a = 123;\n" " int b = (short)-a;;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar46() { // #5491/#5494/#6301 functionVariableUsage("int func() {\n" " int i = 0;\n" " int j{i};\n" " return j;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int func() {\n" " std::mutex m;\n" " std::unique_lock l{ m };\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int func() {\n" " std::shared_lock lock( m );\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar47() { // #6603 functionVariableUsage("void f() {\n" " int (SfxUndoManager::*retrieveCount)(bool) const\n" " = (flag) ? &SfxUndoManager::foo : &SfxUndoManager::bar;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'retrieveCount' is assigned a value that is never used.\n", errout.str()); } void localvar48() { // #6954 functionVariableUsage("void foo() {\n" " long (*pKoeff)[256] = new long[9][256];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar49() { // #7594 functionVariableUsage("class A {\n" " public:\n" " typedef enum { ID1,ID2,ID3 } Id_t;\n" " typedef struct {Id_t id; std::string a; } x_t;\n" " std::vector m_vec;\n" " std::vector Get(void);\n" " void DoSomething();\n" "};\n" "std::vector A::Get(void) {\n" " return m_vec;\n" "}\n" "const std::string Bar() {\n" " return \"x\";\n" "}\n" "void A::DoSomething(void) {\n" " const std::string x = Bar();\n" "}"); ASSERT_EQUALS("[test.cpp:16]: (style) Variable 'x' is assigned a value that is never used.\n", errout.str()); } void localvar50() { // #6261, #6542 // #6261 - ternary operator in function call functionVariableUsage("void foo() {\n" " char buf1[10];\n" " dostuff(cond?buf1:buf2);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo() {\n" " char buf1[10];\n" " dostuff(cond?buf2:buf1);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6542 - ternary operator functionVariableUsage("void foo(int c) {\n" " char buf1[10], buf2[10];\n" " char *p = c ? buf1 : buf2;\n" " dostuff(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar51() { // #8128 FN functionVariableUsage("void foo() {\n" " const char *tok = var->nameToken();\n" " tok = tok->next();\n" // read+write "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'tok' is assigned a value that is never used.\n", errout.str()); // TODO: False negative functionVariableUsage("void foo() {\n" " int x = 4;\n" " x = 15 + x;\n" // read+write "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void localvaralias1() { functionVariableUsage("void foo()\n" "{\n" " int a;\n" " int *b = &a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n" "[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int *b = a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n" "[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " int *b = &a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " char *b = (char *)&a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " char *b = (char *)(&a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " const char *b = (const char *)&a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " const char *b = (const char *)(&a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " char *b = static_cast(&a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a;\n" " const char *b = static_cast(&a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); // a is not a local variable and b is aliased to it functionVariableUsage("int a;\n" "void foo()\n" "{\n" " int *b = &a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); // a is not a local variable and b is aliased to it functionVariableUsage("void foo(int a)\n" "{\n" " int *b = &a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); // a is not a local variable and b is aliased to it functionVariableUsage("class A\n" "{\n" " int a;\n" " void foo()\n" " {\n" " int *b = &a;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int a;\n" "void foo()\n" "{\n" " int *b = &a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo(int a)\n" "{\n" " int *b = &a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class A\n" "{\n" " int a;\n" " void foo()\n" " {\n" " int *b = &a;\n" " *b = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int *b = a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " char *b = (char *)a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " char *b = (char *)(a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " const char *b = (const char *)a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " const char *b = (const char *)(a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " char *b = static_cast(a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " const char *b = static_cast(a);\n" " *b = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int *b = a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int *b = a;\n" " int *c = b;\n" " *c = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int *b = a;\n" " int *c = b;\n" " *c = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int *b = a;\n" " int *c = b;\n" " *c = b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int *b = a;\n" " int *c;\n" " *c = b[0];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'c' is not assigned a value.\n", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int *b = a;\n" " int c = b[0];\n" " x(c);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int *b = a;\n" " int c = b[0];\n" " x(c);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int *b = &a[0];\n" " a[0] = b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int *b = &a[0];\n" " a[0] = b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int *b = a;\n" " a[0] = b[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo(int a[10])\n" "{\n" " int *b = a;\n" " *b = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class A\n" "{\n" " int a[10];\n" " void foo()\n" " {\n" " int *b = a;\n" " *b = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int *b = a;\n" " int *c = b;\n" " *c = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int b[10];\n" " int *c = a;\n" " int *d = b;\n" " *d = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n" "[test.cpp:7]: (style) Variable 'b' is assigned a value that is never used.\n" "[test.cpp:5]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int b[10];\n" " int *c = a;\n" " c = b;\n" " *c = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n" "[test.cpp:7]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10];\n" " int b[10];\n" " int *c = a;\n" " c = b;\n" " *c = 0;\n" " c = a;\n" " *c = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (style) Variable 'a' is assigned a value that is never used.\n" "[test.cpp:7]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10], * b = a + 10;\n" " b[-10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10], * b = a + 10;\n" " b[-10] = 0;\n" " int * c = b - 10;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n" "[test.cpp:5]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10], * b = a + 10;\n" " int * c = b - 10;\n" " x = c[0];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10], * b = a + 10;\n" " int * c = b - 10;\n" " c[1] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10], * b = a + 10;\n" " int * c = b - 10;\n" " c[1] = c[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " int a[10], * b = a + 10;\n" " int * c = b - 10;\n" " int d = c[0];\n" " f(d);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value.\n", errout.str()); functionVariableUsage("void foo() {\n" // #4022 - FP (a is assigned a value that is never used) " int a[2], *b[2];\n" " a[0] = 123;\n" " b[0] = &a[0];\n" " int *d = b[0];\n" " return *d;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo() {\n" // #4022 - FP (a is assigned a value that is never used) " entry a[2], *b[2];\n" " a[0].value = 123;\n" " b[0] = &a[0];\n" " int d = b[0].value;\n" " return d;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct S { char c[100]; };\n" "void foo()\n" "{\n" " char a[100];\n" " struct S * s = (struct S *)a;\n" " s->c[0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct S { char c[100]; };\n" "void foo()\n" "{\n" " char a[100];\n" " struct S * s = (struct S *)a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: a\n" "[test.cpp:5]: (style) Variable 's' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("struct S { char c[100]; };\n" "void foo()\n" "{\n" " char a[100];\n" " const struct S * s = (const struct S *)a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: a\n" "[test.cpp:5]: (style) Variable 's' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("struct S { char c[100]; };\n" "void foo()\n" "{\n" " char a[100];\n" " struct S * s = static_cast(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: a\n" "[test.cpp:5]: (style) Variable 's' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("struct S { char c[100]; };\n" "void foo()\n" "{\n" " char a[100];\n" " const struct S * s = static_cast(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: a\n" "[test.cpp:5]: (style) Variable 's' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int b[10];\n" " int c[10];\n" " int *d;\n" " d = b;\n" " d = a;\n" " d = c;\n" " *d = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: b\n" "[test.cpp:10]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int a[10];\n" "void foo()\n" "{\n" " int b[10];\n" " int c[10];\n" " int *d;\n" " d = b; *d = 0;\n" " d = a; *d = 0;\n" " d = c; *d = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Variable 'b' is assigned a value that is never used.\n" "[test.cpp:9]: (style) Variable 'c' is assigned a value that is never used.\n", errout.str()); } void localvaralias2() { // ticket 1637 functionVariableUsage("void foo()\n" "{\n" " int * a;\n" " x(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias3() { // ticket 1639 functionVariableUsage("void foo()\n" "{\n" " BROWSEINFO info;\n" " char szDisplayName[MAX_PATH];\n" " info.pszDisplayName = szDisplayName;\n" " SHBrowseForFolder(&info);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias4() { // ticket 1643 functionVariableUsage("struct AB { int a; int b; } ab;\n" "void foo()\n" "{\n" " int * a = &ab.a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("struct AB { int a; int b; } ab;\n" "void foo()\n" "{\n" " int * a = &ab.a;\n" " *a = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct AB { int a; int b; };\n" "void foo()\n" "{\n" " struct AB ab;\n" " int * a = &ab.a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ab' is not assigned a value.\n" "[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("struct AB { int a; int b; };\n" "void foo()\n" "{\n" " struct AB ab;\n" " int * a = &ab.a;\n" " *a = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias5() { // ticket 1647 functionVariableUsage("char foo()\n" "{\n" " char buf[8];\n" " char *p = &buf[0];\n" " *p++ = 0;\n" " return buf[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("char foo()\n" "{\n" " char buf[8];\n" " char *p = &buf[1];\n" " *p-- = 0;\n" " return buf[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("char foo()\n" "{\n" " char buf[8];\n" " char *p = &buf[0];\n" " *++p = 0;\n" " return buf[0];\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("char foo()\n" "{\n" " char buf[8];\n" " char *p = &buf[1];\n" " *--p = 0;\n" " return buf[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias6() { // ticket 1729 functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " if (a()) {\n" " buf[0] = 1;\n" " srcdata = buf;\n" " } else {\n" " srcdata = vdata;\n" " }\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " if (a()) {\n" " buf[0] = 1;\n" " srcdata = buf;\n" " srcdata = vdata;\n" " }\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'buf' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " if (a()) {\n" " buf[0] = 1;\n" " srcdata = buf;\n" " }\n" " srcdata = vdata;\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'buf' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " if (a()) {\n" " srcdata = buf;\n" " }\n" " srcdata = vdata;\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: buf\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " if (a()) {\n" " srcdata = vdata;\n" " }\n" " srcdata = buf;\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " char vdata[8];\n" " if (a()) {\n" " buf[0] = 1;\n" " srcdata = buf;\n" " } else {\n" " srcdata = vdata;\n" " }\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " char vdata[8];\n" " if (a()) {\n" " buf[0] = 1;\n" " srcdata = buf;\n" " srcdata = vdata;\n" " }\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Variable 'buf' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " char vdata[8];\n" " if (a()) {\n" " buf[0] = 1;\n" " srcdata = buf;\n" " }\n" " srcdata = vdata;\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Variable 'buf' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " char vdata[8];\n" " if (a()) {\n" " srcdata = buf;\n" " }\n" " srcdata = vdata;\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: buf\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char buf[8];\n" " char *srcdata;\n" " char vdata[8];\n" " if (a()) {\n" " srcdata = vdata;\n" " }\n" " srcdata = buf;\n" " b(srcdata);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Unused variable: vdata\n", errout.str()); } void localvaralias7() { // ticket 1732 functionVariableUsage("void foo()\n" "{\n" " char *c[10];\n" " char **cp;\n" " cp = c;\n" " *cp = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias8() { functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else if (a == 2)\n" " pb = b2;\n" " else if (a == 3)\n" " pb = b3;\n" " else\n" " pb = b4;\n" " b(pb);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else if (a == 2)\n" " pb = b2;\n" " else if (a == 3)\n" " pb = b3;\n" " else {\n" " pb = b1;\n" " pb = b4;\n" " }\n" " b(pb);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else if (a == 2)\n" " pb = b2;\n" " else if (a == 3)\n" " pb = b3;\n" " else {\n" " pb = b1;\n" " pb = b2;\n" " pb = b3;\n" " pb = b4;\n" " }\n" " b(pb);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else if (a == 2)\n" " pb = b2;\n" " else if (a == 3)\n" " pb = b3;\n" " pb = b4;\n" " b(pb);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: b1\n" "[test.cpp:4]: (style) Unused variable: b2\n" "[test.cpp:5]: (style) Unused variable: b3\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else {\n" " if (a == 2)\n" " pb = b2;\n" " else {\n" " if (a == 3)\n" " pb = b3;\n" " else\n" " pb = b4;\n" " }\n" " }\n" " b(pb);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else {\n" " if (a == 2)\n" " pb = b2;\n" " else {\n" " if (a == 3)\n" " pb = b3;\n" " else {\n" " pb = b1;\n" " pb = b4;\n" " }\n" " }\n" " }\n" " b(pb);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else {\n" " if (a == 2)\n" " pb = b2;\n" " else {\n" " if (a == 3)\n" " pb = b3;\n" " else {\n" " pb = b1;\n" " pb = b2;\n" " pb = b3;\n" " pb = b4;\n" " }\n" " }\n" " }\n" " b(pb);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char b1[8];\n" " char b2[8];\n" " char b3[8];\n" " char b4[8];\n" " char *pb;\n" " if (a == 1)\n" " pb = b1;\n" " else {\n" " if (a == 2)\n" " pb = b2;\n" " else {\n" " if (a == 3)\n" " pb = b3;\n" " }\n" " }\n" " pb = b4;\n" " b(pb);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: b1\n" "[test.cpp:4]: (style) Unused variable: b2\n" "[test.cpp:5]: (style) Unused variable: b3\n", errout.str()); } void localvaralias9() { // ticket 1996 functionVariableUsage("void foo()\n" "{\n" " Foo foo;\n" " Foo &ref = foo;\n" " ref[0] = 123;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " Foo foo;\n" " Foo &ref = foo;\n" " ref[0] = 123;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:5]: (style) Variable 'foo' is assigned a value that is never used.\n", errout.str()); } void localvaralias10() { // ticket 2004 functionVariableUsage("void foo(Foo &foo)\n" "{\n" " Foo &ref = foo;\n" " int *x = &ref.x();\n" " *x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo(Foo &foo)\n" "{\n" " Foo &ref = foo;\n" " int *x = &ref.x;\n" " *x = 0;\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); } void localvaralias11() { // #4423 - iterator functionVariableUsage("void f(Foo &foo) {\n" " std::set::iterator x = foo.dostuff();\n" " *(x) = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias12() { // #4394 functionVariableUsage("void f(void) {\n" " int a[4];\n" " int *b = (int*)((int*)a+1);\n" " x(b);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int f(void) {\n" // #4628 " int x=1,y;\n" " y = (x * a) / 100;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvaralias13() { // #4487 functionVariableUsage("void f(char *p) {\n" " char a[4];\n" " p = a;\n" " strcpy(p, \"x\");\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f(char *p) {\n" " char a[4];\n" " p = a;\n" " strcpy(p, \"x\");\n" "}"); TODO_ASSERT_EQUALS("a is assigned value that is never used", "", errout.str()); } void localvaralias14() { // #5619 functionVariableUsage("void f() {\n" " char a[4], *p=a;\n" " p = dostuff(p);\n" "}"); TODO_ASSERT_EQUALS("p is assigned a value that is never used", "", errout.str()); } void localvaralias15() { // #6315 functionVariableUsage("void f() {\n" " int x=3;\n" " int *p = &x;\n" " int *p2[1] = {p};\n" " dostuff(p2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarasm() { functionVariableUsage("void foo(int &b)\n" "{\n" " int a;\n" " asm();\n" " b = a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarStruct1() { functionVariableUsage("void foo()\n" "{\n" " static const struct{ int x, y, w, h; } bounds = {1,2,3,4};\n" " return bounds.x + bounds.y + bounds.w + bounds.h;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarStruct2() { functionVariableUsage("void foo()\n" "{\n" " struct ABC { int a, b, c; };\n" " struct ABC abc = { 1, 2, 3 };\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'abc' is assigned a value that is never used.\n", errout.str()); } void localvarStruct3() { functionVariableUsage("void foo()\n" "{\n" " int a = 10;\n" " union { struct { unsigned char x; }; unsigned char z; };\n" " do {\n" " func();\n" " } while(a--);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: x\n" "[test.cpp:4]: (style) Unused variable: z\n", "", errout.str()); } void localvarStruct5() { functionVariableUsage("int foo() {\n" " A a;\n" " return a.i;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" " A a;\n" " return 0;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:2]: (style) Unused variable: a\n", errout.str()); functionVariableUsage("struct A { int i; };\n" "int foo() {\n" " A a;\n" " return a.i;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class A { int i; };\n" "int foo() {\n" " A a;\n" " return a.i;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct A { int i; };\n" "int foo() {\n" " A a;\n" " a.i = 0;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class A { int i; };\n" "int foo() {\n" " A a;\n" " a.i = 0;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct A { int i; };\n" "int foo() {\n" " A a = { 0 };\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("class A { int i; };\n" "int foo() {\n" " A a = { 0 };\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("class A { int i; public: A(); { } };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct A { int i; };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n", errout.str()); functionVariableUsage("class A { int i; };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n", errout.str()); functionVariableUsage("class A { int i; public: A(); { } };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class A { unknown i; };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class A : public Fred { int i; };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class Fred {char c;};\n" "class A : public Fred { int i; };\n" "int foo() {\n" " A a;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Unused variable: a\n", errout.str()); } void localvarStruct6() { functionVariableUsage("class Type { };\n" "class A {\n" "public:\n" " Type & get() { return t; }\n" "private:\n" " Type t;\n" "};"); ASSERT_EQUALS("", errout.str()); } void localvarStructArray() { // #3633 - detect that struct array is assigned a value functionVariableUsage("void f() {\n" " struct X x[10];\n" " x[0].a = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'x' is assigned a value that is never used.\n", errout.str()); } void localvarOp() { const char op[] = "+-*/%&|^"; for (const char *p = op; *p; ++p) { std::string code("int main()\n" "{\n" " int tmp = 10;\n" " return 123 " + std::string(1, *p) + " tmp;\n" "}"); functionVariableUsage(code.c_str()); ASSERT_EQUALS("", errout.str()); } } void localvarInvert() { functionVariableUsage("int main()\n" "{\n" " int tmp = 10;\n" " return ~tmp;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarIf() { functionVariableUsage("int main()\n" "{\n" " int tmp = 10;\n" " if ( tmp )\n" " return 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarIfElse() { functionVariableUsage("int foo()\n" "{\n" " int tmp1 = 1;\n" " int tmp2 = 2;\n" " int tmp3 = 3;\n" " return tmp1 ? tmp2 : tmp3;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarOpAssign() { functionVariableUsage("void foo()\n" "{\n" " int a = 1;\n" " int b = 2;\n" " a |= b;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo() {\n" " int a = 1;\n" " (b).x += a;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo() {\n" " int a=1, b[10];\n" " b[0] = x;\n" " a += b[0];\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f(int *start, int *stop) {\n" " int length = *start - *stop;\n" " if (length < 10000)\n" " length = 10000;\n" " *stop -= length;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f(int a) {\n" " int x = 3;\n" " a &= ~x;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " Fred fred;\n" " int *a; a = b;\n" " fred += a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarFor() { functionVariableUsage("void foo()\n" "{\n" " int a = 1;\n" " for (;a;);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo() {\n" " for (int i = 0; (pci = cdi_list_get(pciDevices, i)); i++)\n" " {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarForEach() { // #4155 - foreach functionVariableUsage("void foo() {\n" " int i = -1;\n" " int a[] = {1,2,3};\n" " FOREACH_X (int x, a) {\n" " if (i==x) return x;\n" " i = x;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5154 - MSVC 'for each' functionVariableUsage("void f() {\n" " std::map ints;\n" " ints[0]= 1;\n" " for each(std::pair i in ints) { x += i.first; }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarShift1() { functionVariableUsage("int foo()\n" "{\n" " int var = 1;\n" " return 1 >> var;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarShift3() { // #3509 functionVariableUsage("int foo()\n" "{\n" " QList ints;\n" " ints << 1;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" // #4320 " int x;\n" " x << 1;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarCast() { functionVariableUsage("int foo()\n" "{\n" " int a = 1;\n" " int b = static_cast(a);\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarClass() { functionVariableUsage("int foo()\n" "{\n" " class B : public A {\n" " int a;\n" " int f() { return a; }\n" " } b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarUnused() { functionVariableUsage("int foo()\n" "{\n" " bool test __attribute__((unused));\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo()\n" "{\n" " bool test __attribute__((unused)) = true;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo()\n" "{\n" " bool __attribute__((unused)) test;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo()\n" "{\n" " bool __attribute__((unused)) test = true;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo()\n" "{\n" " bool test __attribute__((used));\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo()\n" "{\n" " bool __attribute__((used)) test;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarFunction() { functionVariableUsage("void check_dlsym(void*& h)\n" "{\n" " typedef void (*function_type) (void);\n" " function_type fn;\n" " fn = reinterpret_cast(dlsym(h, \"try_allocation\"));\n" " fn();\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarstatic() { functionVariableUsage("void foo()\n" "{\n" " static int i;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " static int i = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " static int i(0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " static int j = 0;\n" " static int i(j);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("int * foo(int x)\n" "{\n" " static int a[] = { 3, 4, 5, 6 };\n" " static int b[] = { 4, 5, 6, 7 };\n" " static int c[] = { 5, 6, 7, 8 };\n" " b[1] = 1;\n" " return x ? a : c;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " static int i = 0;\n" " if(i < foo())\n" " i += 5;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo() {\n" " static int x = 0;\n" " print(x);\n" " if(x > 5)\n" " x = 0;\n" " else\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarextern() { functionVariableUsage("void foo() {\n" " extern int i;\n" " i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvardynamic1() { functionVariableUsage("void foo()\n" "{\n" " void* ptr = malloc(16);\n" " free(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " void* ptr = g_malloc(16);\n" " g_free(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " void* ptr = kmalloc(16, GFP_KERNEL);\n" " kfree(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " void* ptr = vmalloc(16, GFP_KERNEL);\n" " vfree(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char* ptr = new char[16];\n" " delete[] ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char* ptr = new ( nothrow ) char[16];\n" " delete[] ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char* ptr = new ( std::nothrow ) char[16];\n" " delete[] ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char* ptr = new char;\n" " delete ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " void* ptr = malloc(16);\n" " ptr[0] = 123;\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char* ptr = new char[16];\n" " ptr[0] = 123;\n" " delete[] ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " Fred* fred = new Fred;\n" " std::cout << \"test\" << std::endl;\n" " delete fred;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct Fred { int a; };\n" "void foo()\n" "{\n" " Fred* fred = new Fred;\n" " std::cout << \"test\" << std::endl;\n" " delete fred;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'fred' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int a; Fred() : a(0) {} };\n" "void foo()\n" "{\n" " Fred* fred = new Fred;\n" " std::cout << \"test\" << std::endl;\n" " delete fred;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void foo()\n" "{\n" " Fred* fred = malloc(sizeof(Fred));\n" " std::cout << \"test\" << std::endl;\n" " free(fred);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'fred' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("void foo()\n" "{\n" " char* ptr = names[i];\n" " delete[] ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvardynamic2() { functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " Fred* ptr = malloc(sizeof(Fred));\n" " free(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " Fred* ptr = malloc(sizeof(Fred));\n" " ptr->i = 0;\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " struct Fred* ptr = malloc(sizeof(Fred));\n" " free(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " struct Fred* ptr = malloc(sizeof(Fred));\n" " ptr->i = 0;\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " Fred* ptr = new Fred();\n" " delete ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " Fred* ptr = new (nothrow ) Fred();\n" " delete ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " Fred* ptr = new (std::nothrow) Fred();\n" " delete ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " Fred* ptr = new Fred();\n" " ptr->i = 0;\n" " delete ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " struct Fred* ptr = new Fred();\n" " free(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("struct Fred { int i; };\n" "void foo()\n" "{\n" " struct Fred* ptr = new Fred();\n" " ptr->i = 0;\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class Fred { public: int i; };\n" "void foo()\n" "{\n" " Fred* ptr = malloc(sizeof(Fred));\n" " free(ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("class Fred { public: int i; };\n" "void foo()\n" "{\n" " Fred* ptr = malloc(sizeof(Fred));\n" " ptr->i = 0;\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("class Fred { public: int i; };\n" "void foo()\n" "{\n" " Fred* ptr = new Fred();\n" " delete ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'ptr' is allocated memory that is never used.\n", errout.str()); functionVariableUsage("class Fred { public: int i; };\n" "void foo()\n" "{\n" " Fred* ptr = new Fred();\n" " ptr->i = 0;\n" " delete ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvardynamic3() { // Ticket #3467 - False positive that 'data' is not assigned a value functionVariableUsage("void foo() {\n" " int* data = new int[100];\n" " int* p = data;\n" " for ( int i = 0; i < 10; ++i )\n" " p++;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'p' is modified but its new value is never used.\n", errout.str()); } void localvararray1() { functionVariableUsage("void foo() {\n" " int p[5];\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvararray2() { functionVariableUsage("int foo() {\n" " int p[5][5];\n" " p[0][0] = 0;\n" " return p[0][0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvararray3() { functionVariableUsage("int foo() {\n" " int p[5][5];\n" " *((int*)p[0]) = 0;\n" " return p[0][0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvararray4() { functionVariableUsage("void foo() {\n" " int p[1];\n" " int *pp[0];\n" " p[0] = 1;\n" " *pp[0] = p[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvararray5() { functionVariableUsage("int foo() {\n" " int p[5][5];\n" " dostuff(*p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarstring1() { // ticket #1597 functionVariableUsage("void foo() {\n" " std::string s;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unused variable: s\n", errout.str()); functionVariableUsage("void foo() {\n" " std::string s;\n" " s = \"foo\";\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 's' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo() {\n" " std::string s = \"foo\";\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 's' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("std::string foo() {\n" " std::string s;\n" // Class instances are initialized. Assignment is not necessary " return s;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("std::string foo() {\n" " std::string s = \"foo\";\n" " return s;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarstring2() { // ticket #2929 functionVariableUsage("void foo() {\n" " std::string s;\n" " int i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unused variable: s\n" "[test.cpp:3]: (style) Unused variable: i\n", errout.str()); } void localvarconst1() { functionVariableUsage("void foo() {\n" " const bool b = true;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'b' is assigned a value that is never used.\n", errout.str()); } void localvarconst2() { functionVariableUsage("void foo() {\n" " const int N = 10;\n" " struct X { int x[N]; };\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarthrow() { // ticket #3687 functionVariableUsage("void foo() {\n" " try {}" " catch(Foo& bar) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void localVarStd() { functionVariableUsage("void f() {\n" " std::string x = foo();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'x' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void f() {\n" " std::vector x;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unused variable: x\n", errout.str()); functionVariableUsage("void f() {\n" " std::vector x(100);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'x' is assigned a value that is never used.\n", "", errout.str()); functionVariableUsage("void f() {\n" " std::vector x;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unused variable: x\n", errout.str()); functionVariableUsage("void f() {\n" " std::vector x(100);\n" // Might have a side-effect "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " std::lock_guard lock(mutex_);\n" // Has a side-effect #4385 "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " pLocker = std::shared_ptr(new jfxLocker(m_lock, true));\n" // Could have side-effects (#4355) "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " std::mutex m;\n" " std::unique_lock lock(m);\n" // #4624 "}"); ASSERT_EQUALS("", errout.str()); } // ticket #3104 - false positive when variable is read with "if (NOT var)" void localvarIfNOT() { functionVariableUsage("void f() {\n" " bool x = foo();\n" " if (NOT x) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarAnd() { // #3672 functionVariableUsage("int main() {\n" " unsigned flag = 0x1 << i;\n" " if (m_errorflags & flag) {\n" " return 1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarSwitch() { // #3744 - false positive when used in switch functionVariableUsage("const char *f(int x) {\n" " const char a[] = \"abc\";\n" " const char def[] = \"def\";\n" " const char *ptr;\n" " switch(x) {\n" " case 1: ptr=a; break;\n" " default: ptr=def; break;\n" " }\n" " return ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't write an error that "a" is not used functionVariableUsage("void x() {\n" " unsigned char* pcOctet = NULL;\n" " float fValeur;\n" " switch (pnodeCurrent->left.pnode->usLen) {\n" " case 4:\n" " fValeur = (float)pevalDataLeft->data.fd;\n" " pcOctet = (unsigned char*)&fValeur;\n" " break;\n" " case 8:\n" " pcOctet = (unsigned char*)&pevalDataLeft->data.fd;\n" " break;\n" " }\n" " for (iIndice = 1; iIndice <= (pnodeCurrent->usLen / 2); iIndice++) {\n" " *pcData = gacHexChar[(*pcOctet >> 4) & 0x0F];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't write an error that "fValeur" is not used } void localvarNULL() { // #4203 - Setting NULL value is not redundant - it is safe functionVariableUsage("void f() {\n" " char *p = malloc(100);\n" " foo(p);\n" " free(p);\n" " p = NULL;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarUnusedGoto() { // #4447 functionVariableUsage("bool f(const int &i) {\n" " int X = i;\n" "label:\n" " if ( X == 0 ) {\n" " X -= 101;\n" " return true;\n" " }\n" " if ( X < 1001 ) {\n" " X += 1;\n" " goto label;\n" " }\n" " return false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #4558 functionVariableUsage("int f() {\n" " int i,j=0;\n" " start:\n" " i=j;\n" " i++;\n" " j=i;\n" " if (i<3)\n" " goto start;\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarCppInitialization() { functionVariableUsage("void foo() {\n" " int buf[6];\n" " Data data(buf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarCpp11Initialization() { // #6160 functionVariableUsage("void foo() {\n" " int myNewValue{ 3u };\n" " myManager.theDummyTable.addRow(UnsignedIndexValue{ myNewValue }, DummyRowData{ false });\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarRangeBasedFor() { // #7075 functionVariableUsage("void reset() {\n" " for (auto & e : array)\n" " e = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarAssignInWhile() { functionVariableUsage("void foo() {\n" " int a = 0;\n" " do {\n" " dostuff(a);\n" " } while((a + = x) < 30);\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int foo() {\n" " int var = 1;\n" " while (var = var >> 1) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarTemplate() { functionVariableUsage("template void f() {}\n" "void foo() {\n" " const int x = 0;\n" " f();\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " constexpr std::size_t ArraySize(5);\n" " std::array X; X.dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarFuncPtr() { functionVariableUsage("int main() {\n" " void(*funcPtr)(void)(x);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'funcPtr' is assigned a value never used.\n", "", errout.str()); functionVariableUsage("int main() {\n" " void(*funcPtr)(void);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unused variable: funcPtr\n", errout.str()); functionVariableUsage("int main() {\n" " void(*funcPtr)(void)(x);\n" " funcPtr();\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("int main() {\n" " void(*funcPtr)(void) = x;\n" " funcPtr();\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvarAddr() { // #7747 functionVariableUsage("void f() {\n" " int x = 0;\n" " dostuff(&x);\n" " x = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f() {\n" " int x = 0;\n" " dostuff(std::ref(x));\n" " x = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void chainedAssignment() { // #5466 functionVariableUsage("void NotUsed(double* pdD, int n) {\n" " double sum = 0.0;\n" " for (int i = 0; i\n" "struct Y: Y { };\n" "template<>\n" "struct Y<0> {};\n" "void f() {\n" " Y y;\n" "}"); // #4695 } void usingNamespace() { functionVariableUsage("int foo() {\n" " using namespace ::com::sun::star::i18n;\n" " bool b = false;\n" " int j = 0;\n" " for (int i = 0; i < 3; i++) {\n" " if (!b) {\n" " j = 3;\n" " b = true;\n" " }\n" " }\n" " return j;\n" "}"); // #4585 ASSERT_EQUALS("", errout.str()); } void lambdaFunction() { // #7026 functionVariableUsage("void f() {\n" " bool first = true;\n" "\n" " auto do_something = [&first]() {\n" " if (first) {\n" " first = false;\n" " } else {\n" " dostuff();\n" " }\n" " };\n" " do_something();\n" " do_something();\n" "}"); ASSERT_EQUALS("", errout.str()); // #4956 - assignment in for_each functionVariableUsage("void f(std::vector ints) {\n" " int x = 0;\n" " std::for_each(ints.begin(), ints.end(), [&x](int i){ dostuff(x); x = i; });\n" "}"); ASSERT_EQUALS("", errout.str()); functionVariableUsage("void f(std::vector ints) {\n" " int x = 0;\n" " std::for_each(ints.begin(), ints.end(), [&x](int i){ x += i; });\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'x' is assigned a value that is never used.\n", "", errout.str()); } void namespaces() { // #7557 functionVariableUsage("namespace t { namespace g {\n" " typedef std::pair value;\n" "} }\n" "namespace t { namespace g {} }\n" "namespace t {\n" " inline double getTime() const {\n" " iterator it=find();\n" " double& value=it->second.values[index];\n" " if(isnan(value)) {\n" " value=get();\n" " }\n" " return value;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void bracesInitCpp11() { functionVariableUsage( "int fun() {\n" " static int fpUnread{0};\n" " const int var{fpUnread++};\n" " return var;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestUnusedVar) cppcheck-1.82/test/testutils.h000066400000000000000000000037151322667425100164320ustar00rootroot00000000000000/* * 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 TestUtilsH #define TestUtilsH #include "settings.h" #include "tokenize.h" class Token; class givenACodeSampleToTokenize { private: Settings _settings; Tokenizer _tokenizer; public: explicit givenACodeSampleToTokenize(const char sample[], bool createOnly = false, bool cpp = true) : _tokenizer(&_settings, 0) { std::istringstream iss(sample); if (createOnly) _tokenizer.list.createTokens(iss, cpp ? "test.cpp" : "test.c"); else _tokenizer.tokenize(iss, cpp ? "test.cpp" : "test.c"); } const Token* tokens() const { return _tokenizer.tokens(); } }; class SimpleSuppressor : public ErrorLogger { public: SimpleSuppressor(Settings &settings, ErrorLogger *next) : _settings(settings), _next(next) { } virtual void reportOut(const std::string &outmsg) { _next->reportOut(outmsg); } virtual void reportErr(const ErrorLogger::ErrorMessage &msg) { if (!msg._callStack.empty() && !_settings.nomsg.isSuppressed(msg._id, msg._callStack.begin()->getfile(), msg._callStack.begin()->line)) _next->reportErr(msg); } private: Settings &_settings; ErrorLogger *_next; }; #endif // TestUtilsH cppcheck-1.82/test/testvaarg.cpp000066400000000000000000000322321322667425100167210ustar00rootroot00000000000000/* * 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 "checkvaarg.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestVaarg : public TestFixture { public: TestVaarg() : TestFixture("TestVaarg") {} 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.. CheckVaarg checkVaarg(&tokenizer, &settings, this); checkVaarg.runSimplifiedChecks(&tokenizer, &settings, this); } void run() { settings.addEnabled("warning"); TEST_CASE(wrongParameterTo_va_start); TEST_CASE(referenceAs_va_start); TEST_CASE(va_end_missing); TEST_CASE(va_list_usedBeforeStarted); TEST_CASE(va_start_subsequentCalls); TEST_CASE(unknownFunctionScope); } void wrongParameterTo_va_start() { check("void Format(char* szFormat, char* szBuffer, size_t nSize, ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szFormat);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) 'szFormat' given to va_start() is not last named argument of the function. Did you intend to pass 'nSize'?\n", errout.str()); check("void Format(char* szFormat, char* szBuffer, size_t nSize, ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) 'szBuffer' given to va_start() is not last named argument of the function. Did you intend to pass 'nSize'?\n", errout.str()); check("void Format(char* szFormat, char* szBuffer, size_t nSize, ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, nSize);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char *argv[]) {\n" " long(^addthem)(const char *, ...) = ^long(const char *format, ...) {\n" " va_list argp;\n" " va_start(argp, format);\n" " c = va_arg(argp, int);\n" " };\n" "}"); // Don't crash (#6032) ASSERT_EQUALS("[test.cpp:6]: (error) va_list 'argp' was opened but not closed by va_end().\n", errout.str()); check("void Format(char* szFormat, char* szBuffer, size_t nSize, ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr);\n" " va_end(arg_ptr);\n" "}"); // Don't crash if less than expected arguments are given. ASSERT_EQUALS("", errout.str()); check("void assertf_fail(const char *assertion, const char *file, int line, const char *func, const char* msg, ...) {\n" " struct A {\n" " A(char* buf, int size) {}\n" " void printf(const char * format, ...) {\n" " va_list args;\n" " va_start(args, format);\n" " va_end(args);\n" " }\n" " };\n" "}"); // Inner class (#6453) ASSERT_EQUALS("", errout.str()); } void referenceAs_va_start() { check("void Format(char* szFormat, char (&szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Using reference 'szBuffer' as parameter for va_start() results in undefined behaviour.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void va_end_missing() { check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6186 check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " try {\n" " throw sth;\n" " } catch(...) {\n" " va_end(arg_ptr);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) va_list 'arg_ptr' was opened but not closed by va_end().\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " if(sth) return;\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) va_list 'arg_ptr' was opened but not closed by va_end().\n", errout.str()); } void va_list_usedBeforeStarted() { check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " return va_arg(arg_ptr, float);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " foo(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_copy(f, arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" " return va_arg(arg_ptr, float);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) va_list 'arg_ptr' used before va_start() was called.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" " va_start(arg_ptr, szBuffer);\n" " foo(va_arg(arg_ptr, float));\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6066 check("void Format(va_list v1) {\n" " va_list v2;\n" " va_copy(v2, v1);\n" " foo(va_arg(v1, float));\n" " va_end(v2);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7527 check("void foo(int flag1, int flag2, ...) {\n" " switch (flag1) {\n" " default:\n" " va_list vargs;\n" " va_start(vargs, flag2);\n" " if (flag2) {\n" " va_end(vargs);\n" " break;\n" " }\n" " int data = va_arg(vargs, int);\n" " va_end(vargs);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #7533 check("void action_push(int type, ...) {\n" " va_list args;\n" " va_start(args, type);\n" " switch (push_mode) {\n" " case UNDO:\n" " list_add(&act->node, &to_redo);\n" " break;\n" " case REDO:\n" " list_add(&act->node, &to_undo);\n" " break;\n" " }\n" " va_end(args);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void action_push(int type, ...) {\n" " va_list args;\n" " va_start(args, type);\n" " switch (push_mode) {\n" " case UNDO:\n" " list_add(&act->node, &to_redo);\n" " va_end(args);\n" " break;\n" " case REDO:\n" " list_add(&act->node, &to_undo);\n" " va_end(args);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void action_push(int type, ...) {\n" " va_list args;\n" " va_start(args, type);\n" " switch (push_mode) {\n" " case UNDO:\n" " list_add(&act->node, &to_redo);\n" " break;\n" " case REDO:\n" " list_add(&act->node, &to_undo);\n" " va_end(args);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (error) va_list 'args' was opened but not closed by va_end().\n", errout.str()); // #8043 check("void redisvFormatCommand(char *format, va_list ap, bool flag) {\n" " va_list _cpy;\n" " va_copy(_cpy, ap);\n" " if (flag)\n" " goto fmt_valid;\n" " va_end(_cpy);\n" " goto format_err;\n" "fmt_valid:\n" " sdscatvprintf(curarg, _format, _cpy);\n" " va_end(_cpy);\n" "format_err:\n" "}"); ASSERT_EQUALS("", errout.str()); } void va_start_subsequentCalls() { check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) va_start() or va_copy() called subsequently on 'arg_ptr' without va_end() in between.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list vl1;\n" " va_start(vl1, szBuffer);\n" " va_copy(vl1, vl1);\n" " va_end(vl1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) va_start() or va_copy() called subsequently on 'vl1' without va_end() in between.\n", errout.str()); check("void Format(char* szFormat, char (*szBuffer)[_Size], ...) {\n" " va_list arg_ptr;\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" " va_start(arg_ptr, szBuffer);\n" " va_end(arg_ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void unknownFunctionScope() { check("void BG_TString::Format() {\n" " BG_TChar * f;\n" " va_start(args,f);\n" " BG_TString result(f);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7559 check("void mowgli_object_message_broadcast(mowgli_object_t *self, const char *name, ...) {\n" " va_list va;\n" " MOWGLI_LIST_FOREACH(n, self->klass->message_handlers.head) {\n" " if (!strcasecmp(sig2->name, name))\n" " break;\n" " }\n" " va_start(va, name);\n" " va_end(va);\n" "}"); } }; REGISTER_TEST(TestVaarg) cppcheck-1.82/test/testvalueflow.cpp000066400000000000000000002732431322667425100176360ustar00rootroot00000000000000/* * 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 "library.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include #include #include #include class TestValueFlow : public TestFixture { public: TestValueFlow() : TestFixture("TestValueFlow") { } private: Settings settings; void run() { // strcpy cfg const char cfg[] = "\n" "\n" " \n" ""; settings.library.loadxmldata(cfg, sizeof(cfg)); TEST_CASE(valueFlowNumber); TEST_CASE(valueFlowString); TEST_CASE(valueFlowPointerAlias); TEST_CASE(valueFlowArrayElement); TEST_CASE(valueFlowMove); TEST_CASE(valueFlowBitAnd); TEST_CASE(valueFlowCalculations); TEST_CASE(valueFlowErrorPath); TEST_CASE(valueFlowBeforeCondition); TEST_CASE(valueFlowBeforeConditionAndAndOrOrGuard); TEST_CASE(valueFlowBeforeConditionAssignIncDec); TEST_CASE(valueFlowBeforeConditionFunctionCall); TEST_CASE(valueFlowBeforeConditionGlobalVariables); TEST_CASE(valueFlowBeforeConditionGoto); TEST_CASE(valueFlowBeforeConditionIfElse); TEST_CASE(valueFlowBeforeConditionLoop); TEST_CASE(valueFlowBeforeConditionMacro); TEST_CASE(valueFlowBeforeConditionSizeof); TEST_CASE(valueFlowBeforeConditionSwitch); TEST_CASE(valueFlowBeforeConditionTernaryOp); TEST_CASE(valueFlowAfterAssign); TEST_CASE(valueFlowAfterCondition); TEST_CASE(valueFlowForwardCompoundAssign); TEST_CASE(valueFlowForwardCorrelatedVariables); TEST_CASE(valueFlowForwardFunction); TEST_CASE(valueFlowForwardLambda); TEST_CASE(valueFlowSwitchVariable); TEST_CASE(valueFlowForLoop); TEST_CASE(valueFlowSubFunction); TEST_CASE(valueFlowFunctionReturn); TEST_CASE(valueFlowFunctionDefaultParameter); TEST_CASE(knownValue); TEST_CASE(valueFlowSizeofForwardDeclaredEnum); TEST_CASE(valueFlowGlobalVar); TEST_CASE(valueFlowGlobalStaticVar); TEST_CASE(valueFlowInlineAssembly); TEST_CASE(valueFlowUninit); } bool testValueOfX(const char code[], unsigned int linenr, int value) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "x" && tok->linenr() == linenr) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (it->isIntValue() && it->intvalue == value) return true; } } } return false; } bool testValueOfX(const char code[], unsigned int linenr, float value, float diff) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "x" && tok->linenr() == linenr) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (it->isFloatValue() && it->floatValue >= value - diff && it->floatValue <= value + diff) return true; } } } return false; } std::string getErrorPathForX(const char code[], unsigned int linenr) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() != "x" || tok->linenr() != linenr) continue; std::ostringstream ostr; std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { for (ValueFlow::Value::ErrorPath::const_iterator ep = it->errorPath.begin(); ep != it->errorPath.end(); ++ep) { const Token *eptok = ep->first; const std::string &msg = ep->second; ostr << eptok->linenr() << ',' << msg << '\n'; } } return ostr.str(); } return ""; } bool testValueOfX(const char code[], unsigned int linenr, const char value[]) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "x" && tok->linenr() == linenr) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (it->isTokValue() && Token::simpleMatch(it->tokvalue, value)) return true; } } } return false; } bool testValueOfX(const char code[], unsigned int linenr, ValueFlow::Value::MoveKind moveKind) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "x" && tok->linenr() == linenr) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (it->isMovedValue() && it->moveKind == moveKind) return true; } } } return false; } bool testConditionalValueOfX(const char code[], unsigned int linenr, int value) { // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "x" && tok->linenr() == linenr) { std::list::const_iterator it; for (it = tok->values().begin(); it != tok->values().end(); ++it) { if (it->isIntValue() && it->intvalue == value && it->condition) return true; } } } return false; } void bailout(const char code[]) { settings.debugwarnings = true; errout.str(""); std::vector files; files.push_back("test.cpp"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); 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(""); settings.debugwarnings = false; } std::list tokenValues(const char code[], const char tokstr[]) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); errout.str(""); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr); return tok ? tok->values() : std::list(); } ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) { std::list values = tokenValues(code, tokstr); return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value(); } void valueFlowNumber() { ASSERT_EQUALS(123, valueOfTok("x=123;", "123").intvalue); ASSERT(std::fabs(valueOfTok("x=0.5;", "0.5").floatValue - 0.5f) < 0.1f); ASSERT_EQUALS(10, valueOfTok("enum {A=10,B=15}; x=A+0;", "+").intvalue); ASSERT_EQUALS(0, valueOfTok("x=false;", "false").intvalue); ASSERT_EQUALS(1, valueOfTok("x=true;", "true").intvalue); ASSERT_EQUALS(0, valueOfTok("x(NULL);", "NULL").intvalue); ASSERT_EQUALS((int)('a'), valueOfTok("x='a';", "'a'").intvalue); ASSERT_EQUALS((int)('\n'), valueOfTok("x='\\n';", "'\\n'").intvalue); ASSERT_EQUALS(0xFFFFFFFF00000000, valueOfTok("x=0xFFFFFFFF00000000;","18446744069414584320U").intvalue); // #7701 } void valueFlowString() { const char *code; // valueFlowAfterAssign code = "const char * f() {\n" " static const char *x;\n" " if (a) x = \"123\";\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\"")); // valueFlowSubFunction code = "void dostuff(const char *x) {\n" " f(x);\n" "}\n" "\n" "void test() { dostuff(\"abc\"); }"; ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\"")); } void valueFlowPointerAlias() { const char *code; code = "const char * f() {\n" " static const char *x;\n" " static char ret[10];\n" " if (a) x = &ret[0];\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]")); // dead pointer code = "void f() {\n" " int *x;\n" " if (cond) { int i; x = &i; }\n" " *x = 0;\n" // <- x can point at i "}"; ASSERT_EQUALS(true, testValueOfX(code, 4, "& i")); code = "void f() {\n" " struct X *x;\n" " x = &x[1];\n" "}"; ASSERT_EQUALS(true, tokenValues(code, "&").empty()); ASSERT_EQUALS(true, tokenValues(code, "x [").empty()); } void valueFlowArrayElement() { const char *code; code = "void f() {\n" " const int x[] = {43,23,12};\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, "{ 43 , 23 , 12 }")); code = "void f() {\n" " const char x[] = \"abcd\";\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, "\"abcd\"")); code = "void f() {\n" " char x[32] = \"abcd\";\n" " return x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, "\"abcd\"")); code = "void f() {\n" " int a[10];\n" " int *x = a;\n" // <- a value is a " *x = 0;\n" // .. => x value is a "}"; ASSERT_EQUALS(true, testValueOfX(code, 4, "a")); code = "char f() {\n" " const char *x = \"abcd\";\n" " return x[0];\n" "}"; ASSERT_EQUALS((int)('a'), valueOfTok(code, "[").intvalue); code = "char f() {\n" " const char *x = \"\";\n" " return x[0];\n" "}"; ASSERT_EQUALS(0, valueOfTok(code, "[").intvalue); } void valueFlowMove() { const char *code; code = "void f() {\n" " X x;\n" " g(std::move(x));\n" " y=x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MovedVariable)); code = "void f() {\n" " X x;\n" " g(std::forward(x));\n" " y=x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::ForwardedVariable)); code = "void f() {\n" " X x;\n" " g(std::move(x).getA());\n" // Only parts of x might be moved out " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MovedVariable)); code = "void f() {\n" " X x;\n" " g(std::forward(x).getA());\n" // Only parts of x might be moved out " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::ForwardedVariable)); code = "void f() {\n" " X x;\n" " g(std::move(x));\n" " x.clear();\n" " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MovedVariable)); code = "void f() {\n" " X x;\n" " g(std::move(x));\n" " y=x->y;\n" " z=x->z;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, ValueFlow::Value::MovedVariable)); code = "void f(int i) {\n" " X x;\n" " z = g(std::move(x));\n" " y = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MovedVariable)); code = "void f(int i) {\n" " X x;\n" " y = g(std::move(x), \n" " x.size());\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MovedVariable)); code = "void f(int i) {\n" " X x;\n" " x = g(std::move(x));\n" " y = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MovedVariable)); code = "A f(int i) {\n" " X x;\n" " if (i)" " return g(std::move(x));\n" " return h(std::move(x));\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MovedVariable)); } void valueFlowCalculations() { const char *code; // Different operators ASSERT_EQUALS(5, valueOfTok("3 + (a ? b : 2);", "+").intvalue); ASSERT_EQUALS(1, valueOfTok("3 - (a ? b : 2);", "-").intvalue); ASSERT_EQUALS(6, valueOfTok("3 * (a ? b : 2);", "*").intvalue); ASSERT_EQUALS(6, valueOfTok("13 / (a ? b : 2);", "/").intvalue); ASSERT_EQUALS(1, valueOfTok("13 % (a ? b : 2);", "%").intvalue); ASSERT_EQUALS(0, valueOfTok("3 == (a ? b : 2);", "==").intvalue); ASSERT_EQUALS(1, valueOfTok("3 != (a ? b : 2);", "!=").intvalue); ASSERT_EQUALS(1, valueOfTok("3 > (a ? b : 2);", ">").intvalue); ASSERT_EQUALS(1, valueOfTok("3 >= (a ? b : 2);", ">=").intvalue); ASSERT_EQUALS(0, valueOfTok("3 < (a ? b : 2);", "<").intvalue); ASSERT_EQUALS(0, valueOfTok("3 <= (a ? b : 2);", "<=").intvalue); ASSERT_EQUALS(1, valueOfTok("(UNKNOWN_TYPE)1;","(").intvalue); ASSERT(tokenValues("(UNKNOWN_TYPE)1000;","(").empty()); // don't know if there is truncation, sign extension ASSERT_EQUALS(255, valueOfTok("(unsigned char)~0;", "(").intvalue); ASSERT_EQUALS(0, valueOfTok("(int)0;", "(").intvalue); ASSERT_EQUALS(3, valueOfTok("(int)(1+2);", "(").intvalue); ASSERT_EQUALS(0, valueOfTok("(UNKNOWN_TYPE*)0;","(").intvalue); ASSERT_EQUALS(100, valueOfTok("(int)100.0;", "(").intvalue); // Don't calculate if there is UB ASSERT(tokenValues(";-1<<10;","<<").empty()); ASSERT(tokenValues(";10<<-1;","<<").empty()); ASSERT(tokenValues(";10<<64;","<<").empty()); ASSERT(tokenValues(";-1>>10;",">>").empty()); ASSERT(tokenValues(";10>>-1;",">>").empty()); ASSERT(tokenValues(";10>>64;",">>").empty()); // calculation using 1,2 variables/values code = "void f(int x) {\n" " a = x+456;\n" " if (x==123) {}" "}"; ASSERT_EQUALS(579, valueOfTok(code, "+").intvalue); code = "void f(int x, int y) {\n" " a = x+y;\n" " if (x==123 || y==456) {}" "}"; ASSERT_EQUALS(0, valueOfTok(code, "+").intvalue); code = "void f(int x) {\n" " a = x+x;\n" " if (x==123) {}" "}"; ASSERT_EQUALS(246, valueOfTok(code, "+").intvalue); code = "void f(int x, int y) {\n" " a = x*x;\n" " if (x==2) {}\n" " if (x==4) {}\n" "}"; std::list values = tokenValues(code,"*"); ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(4, values.front().intvalue); ASSERT_EQUALS(16, values.back().intvalue); code = "void f(int x) {\n" " if (x == 3) {}\n" " a = x * (1 - x - 1);\n" "}"; ASSERT_EQUALS(-9, valueOfTok(code, "*").intvalue); // addition of different variables with known values code = "int f(int x) {\n" " int a = 1;\n" " while (x!=3) { x+=a; }\n" " return x/a;\n" "}\n"; ASSERT_EQUALS(3, valueOfTok(code, "/").intvalue); // ? : code = "x = y ? 2 : 3;\n"; values = tokenValues(code,"?"); ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); ASSERT_EQUALS(3, values.back().intvalue); code = "void f(int a) { x = a ? 2 : 3; }\n"; values = tokenValues(code,"?"); ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); ASSERT_EQUALS(3, values.back().intvalue); code = "x = (2<5) ? 2 : 3;\n"; values = tokenValues(code, "?"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); code = "x = 123 ? : 456;\n"; values = tokenValues(code, "?"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(123, values.empty() ? 0 : values.front().intvalue); // ~ code = "x = ~0U;"; settings.platform(cppcheck::Platform::Native); // ensure platform is native values = tokenValues(code,"~"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(~0U, values.back().intvalue); // ! code = "void f(int x) {\n" " a = !x;\n" " if (x==0) {}\n" "}"; values = tokenValues(code,"!"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.back().intvalue); // unary minus code = "void f(int x) {\n" " a = -x;\n" " if (x==10) {}\n" "}"; values = tokenValues(code,"-"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(-10, values.back().intvalue); // sizeof code = "void f() {\n" " x = sizeof(int);\n" "}"; values = tokenValues(code,"( int )"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(settings.sizeof_int, values.back().intvalue); code = "void f() {\n" " struct S *a[10];" " x = sizeof(a) / sizeof(a[0]);\n" "}"; values = tokenValues(code,"/"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(10, values.back().intvalue); // function call => calculation code = "void f(int x) {\n" " a = x + 8;\n" "}\n" "void callf() {\n" " f(7);\n" "}"; ASSERT_EQUALS(15, valueOfTok(code, "+").intvalue); code = "void f(int x, int y) {\n" " a = x + y;\n" "}\n" "void callf() {\n" " f(1,1);\n" " f(10,10);\n" "}"; values = tokenValues(code, "+"); ASSERT_EQUALS(true, values.empty()); if (!values.empty()) { /* todo.. */ ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); ASSERT_EQUALS(22, values.back().intvalue); } // Comparison of string values = tokenValues("f(\"xyz\" == \"xyz\");", "=="); // implementation defined ASSERT_EQUALS(0U, values.size()); // <- no value values = tokenValues("f(\"xyz\" == 0);", "=="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(0, values.front().intvalue); values = tokenValues("f(0 == \"xyz\");", "=="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(0, values.front().intvalue); values = tokenValues("f(\"xyz\" != 0);", "!="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.front().intvalue); values = tokenValues("f(0 != \"xyz\");", "!="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.front().intvalue); } void valueFlowErrorPath() { const char *code; code = "void f() {\n" " int x = 53;\n" " a = x;\n" "}\n"; ASSERT_EQUALS("2,Assignment 'x=53', assigned value is 53\n", getErrorPathForX(code, 3U)); code = "void f(int y) {\n" " int x = y;\n" " a = x;\n" " y += 12;\n" " if (y == 32) {}" "}\n"; ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n" "4,Compound assignment '+=', before assignment value is 20\n" "2,Assignment 'x=y', assigned value is 20\n", getErrorPathForX(code, 3U)); code = "void f1(int x) {\n" " a = x;\n" "}\n" "void f2() {\n" " int x = 3;\n" " f1(x+1);\n" "}\n"; ASSERT_EQUALS("5,Assignment 'x=3', assigned value is 3\n" "6,Calling function 'f1', 1st argument 'x' value is 4\n", getErrorPathForX(code, 2U)); code = "void f(int a) {\n" " int x;\n" " for (x = a; x < 50; x++) {}\n" " b = x;\n" "}\n"; ASSERT_EQUALS("3,After for loop, x has value 50\n", getErrorPathForX(code, 4U)); } void valueFlowBeforeCondition() { const char *code; code = "void f(int x) {\n" " int a = x;\n" " if (x == 123) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 123)); code = "void f(unsigned int x) {\n" " int a = x;\n" " if (x >= 1) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 1)); ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "void f(unsigned int x) {\n" " int a = x;\n" " if (x > 0) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "void f(unsigned int x) {\n" " int a = x;\n" " if (x > 1) {}\n" // not zero => don't consider > condition "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 1)); code = "void f(int x) {\n" // not unsigned => don't consider > condition " int a = x;\n" " if (x > 0) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); code = "void f(int *x) {\n" " *x = 100;\n" " if (x) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "extern const int x;\n" "void f() {\n" " int a = x;\n" " if (x == 123) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); } void valueFlowBeforeConditionAssignIncDec() { // assignment / increment const char *code; code = "void f(int x) {\n" " x = 2 + x;\n" " if (x == 65);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 65)); code = "void f(int x) {\n" " x = y = 2 + x;\n" " if (x == 65);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 65)); code = "void f(int x) {\n" " a[x++] = 0;\n" " if (x == 5);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 5)); code = "void f(int x) {\n" " a = x;\n" " x++;\n" " if (x == 4);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 3)); ASSERT_EQUALS("4,Assuming that condition 'x==4' is not redundant\n" "3,x is incremented, before this increment the value is 3\n", getErrorPathForX(code, 2U)); // compound assignment += , -= , ... code = "void f(int x) {\n" " a = x;\n" " x += 2;\n" " if (x == 4);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 2)); code = "void f(int x) {\n" " a = x;\n" " x -= 2;\n" " if (x == 4);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 6)); code = "void f(int x) {\n" " a = x;\n" " x *= 2;\n" " if (x == 42);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 21)); code = "void f(int x) {\n" " a = x;\n" " x /= 5;\n" " if (x == 42);\n" "}"; ASSERT(tokenValues(code, "x ;").empty()); // bailout: assignment bailout("void f(int x) {\n" " x = y;\n" " if (x == 123) {}\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1035:valueFlowReverse bailout: assignment of x\n", errout.str()); } void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by && const char *code; code = "void f(int x) {\n" " if (!x || \n" // <- x can be 0 " a/x) {}\n" // <- x can't be 0 " if (x==0) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f(int *x) {\n" " ((x=ret())&&\n" " (*x==0));\n" // <- x is not 0 " if (x==0) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f(int *x) {\n" " int a = (x && *x == '1');\n" " int b = a ? atoi(x) : 0;\n" // <- x is not 0 " if (x==0) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); } void valueFlowBeforeConditionFunctionCall() { // function calls const char *code; code = "void f(int x) {\n" " a = x;\n" " setx(x);\n" " if (x == 1) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX((std::string("void setx(int x);")+code).c_str(), 2U, 1)); ASSERT_EQUALS(false, testValueOfX((std::string("void setx(int &x);")+code).c_str(), 2U, 1)); ASSERT_EQUALS(true, testValueOfX(code, 2U, 1)); code = "void f(char* x) {\n" " strcpy(x,\"abc\");\n" " if (x) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "void addNewFunction(Scope**scope, const Token**tok);\n" "void f(Scope *x) {\n" " x->functionList.back();\n" " addNewFunction(&x,&tok);\n" // address-of, x can be changed by subfunction " if (x) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); } void valueFlowBeforeConditionLoop() { // while, for, do-while const char *code; code = "void f(int x) {\n" // loop condition, x is not assigned inside loop => use condition " a = x;\n" // x can be 37 " while (x == 37) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 37)); code = "void f(int x) {\n" // loop condition, x is assigned inside loop => don't use condition " a = x;\n" // don't assume that x can be 37 " while (x != 37) { x++; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 37)); code = "void f(int x) {\n" " a = x;\n" " for (; x!=1; x++) { }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 1)); code = "void f(menu *x) {\n" " a = x->parent;\n" " for (i=0;(i<10) && (x!=0); i++) { x = x->next; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); code = "void f(int x) {\n" // condition inside loop, x is NOT assigned inside loop => use condition " a = x;\n" " do {\n" " if (x==76) {}\n" " } while (1);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 76)); code = "void f(int x) {\n" // conditions inside loop, x is assigned inside do-while => don't use condition " a = x;\n" " do {\n" " if (x!=76) { x=do_something(); }\n" " } while (1);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 76)); code = "void f(X x) {\n" // conditions inside loop, x is assigned inside do-while => don't use condition " a = x;\n" " for (i=1;i<=count;i++) {\n" " BUGON(x==0)\n" " x = x.next;\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); } void valueFlowBeforeConditionTernaryOp() { // bailout: ?: const char *code; bailout("void f(int x) {\n" " y = ((x<0) ? x : ((x==2)?3:4));\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str()); bailout("int f(int x) {\n" " int r = x ? 1 / x : 0;\n" " if (x == 0) {}\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str()); code = "void f(int x) {\n" " int a =v x;\n" " a = b ? x/2 : 20/x;\n" " if (x == 123) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 123)); code = "void f(const s *x) {\n" " x->a = 0;\n" " if (x ? x->a : 0) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "void f(int x, int y) {\n" " a = x;\n" " if (y){}\n" " if (x==123){}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 123)); } void valueFlowBeforeConditionSizeof() { // skip sizeof const char *code; code = "void f(int *x) {\n" " sizeof(x[0]);\n" " if (x==63){}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 63)); code = "void f(int *x) {\n" " char a[sizeof x.y];\n" " if (x==0){}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); } void valueFlowBeforeConditionIfElse() { // bailout: if/else/etc const char *code; code = "void f(X * x) {\n" " a = x;\n" " if ((x != NULL) &&\n" " (a(x->name, html)) &&\n" " (a(x->name, body))) {}\n" " if (x != NULL) { }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); bailout("void f(int x) {\n" " if (x != 123) { b = x; }\n" " if (x == 123) {}\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1144:valueFlowReverse bailout: variable x stopping on }\n", errout.str()); code = "void f(int x) {\n" " a = x;\n" " if (abc) { x = 1; }\n" // <- condition must be false if x is 7 in next line " if (x == 7) { }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 7)); } void valueFlowBeforeConditionGlobalVariables() { const char *code; // handle global variables code = "int x;\n" "void f() {\n" " int a = x;\n" " if (x == 123) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3,123)); // bailout when there is function call code = "class Fred { int x; void clear(); void f(); };\n" "void Fred::f() {\n" " int a = x;\n" " clear();\n" // <- x might be assigned " if (x == 234) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code,3,234)); } void valueFlowBeforeConditionSwitch() { // bailout: switch // TODO : handle switch/goto more intelligently bailout("void f(int x, int y) {\n" " switch (y) {\n" " case 1: a=x; break;\n" " case 2: if (x==5) {} break;\n" " };\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on break\n", errout.str()); bailout("void f(int x, int y) {\n" " switch (y) {\n" " case 1: a=x; return 1;\n" " case 2: if (x==5) {} break;\n" " };\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on return\n", errout.str()); } void valueFlowBeforeConditionMacro() { // bailout: condition is a expanded macro bailout("#define M if (x==123) {}\n" "void f(int x) {\n" " a = x;\n" " M;\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str()); } void valueFlowBeforeConditionGoto() { // bailout: goto label (TODO: handle gotos more intelligently) bailout("void f(int x) {\n" " if (x == 123) { goto out; }\n" " a=x;\n" // <- x is not 123 "out:" " if (x==123){}\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:4]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable x stopping on goto label\n" "[test.cpp:2]: (debug) valueflow.cpp:1813:valueFlowForward bailout: variable x. noreturn conditional scope.\n" , errout.str()); // #5721 - FP bailout("static void f(int rc) {\n" " ABC* abc = getabc();\n" " if (!abc) { goto out };\n" "\n" " abc->majortype = 0;\n" " if (FAILED(rc)) {}\n" "\n" "out:\n" " if (abc) {}\n" "}\n"); ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1035:valueFlowReverse bailout: assignment of abc\n" "[test.cpp:8]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable abc stopping on goto label\n" "[test.cpp:3]: (debug) valueflow.cpp:1813:valueFlowForward bailout: variable abc. noreturn conditional scope.\n", errout.str()); } void valueFlowAfterAssign() { const char *code; code = "void f() {\n" " int x = 123;\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f() {\n" " bool x = 32;\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); code = "void f() {\n" " int x = 123;\n" " a = sizeof(x);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f() {\n" " int x = 123;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f() {\n" " int x = 9;\n" " --x;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 9)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 8)); ASSERT_EQUALS("2,Assignment 'x=9', assigned value is 9\n" "3,x is decremented', new value is 8\n", getErrorPathForX(code, 4U)); code = "void x() {\n" " int x = value ? 6 : 0;\n" " x =\n" " 1 + x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 7)); code = "void f() {\n" " int x = 0;\n" " y = x += z;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " static int x = 2;\n" " x++;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); code = "void f() {\n" " static int x = 2;\n" " a >> x;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); code = "void f() {\n" " static int x = 0;\n" " if (x==0) x = getX();\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // function code = "void f() {\n" " char *x = 0;\n" " int success = getx((char**)&x);\n" " if (success) x[0] = 0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " char *x = 0;\n" " getx(reinterpret_cast(&x));\n" " *x = 0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // lambda code = "void f() {\n" " int x = 0;\n" " Q q = [&]() {\n" " if (x > 0) {}\n" " x++;\n" " };\n" " dosomething(q);\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // ?: code = "void f() {\n" " int x = 8;\n" " a = ((x > 10) ?\n" " x : 0);\n" // <- x is not 8 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 8)); code = "void f() {\n" // #6973 " char *x = \"\";\n" " a = ((x[0] == 'U') ?\n" " x[1] : 0);\n" // <- x is not "" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, "\"\"")); code = "void f() {\n" // #6973 " char *x = getenv (\"LC_ALL\");\n" " if (x == NULL)\n" " x = \"\";\n" "\n" " if ( (x[0] == 'U') &&\n" // x can be "" " (x[1] ?\n" // x can't be "" " x[3] :\n" // x can't be "" " x[2] ))\n" // x can't be "" " {}\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 6U, "\"\"")); ASSERT_EQUALS(false, testValueOfX(code, 7U, "\"\"")); ASSERT_EQUALS(false, testValueOfX(code, 8U, "\"\"")); ASSERT_EQUALS(false, testValueOfX(code, 9U, "\"\"")); code = "void f() {\n" // #7599 " t *x = 0;\n" " y = (a ? 1 : x\n" // <- x is 0 " && x->y ? 1 : 2);" // <- x is not 0 "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #7599 " t *x = 0;\n" " y = (a ? 1 : !x\n" // <- x is 0 " || x->y ? 1 : 2);" // <- x is not 0 "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // if/else code = "void f() {\n" " int x = 123;\n" " if (condition) return;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f() {\n" " int x = 1;\n" " if (condition) x = 2;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 1)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 2)); code = "void f() {\n" " int x = 123;\n" " if (condition1) x = 456;\n" " if (condition2) x = 789;\n" " a = 2 + x;\n" // <- either assignment "x=123" is redundant or x can be 123 here. "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 123)); code = "void f(int a) {\n" " int x = 123;\n" " if (a > 1)\n" " ++x;\n" " else\n" " ++x;\n" " return 2 + x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); code = "void f() {\n" " int x = 1;\n" " if (condition1) x = 2;\n" " else return;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 1)); code = "void f(){\n" " int x = 0;\n" " if (a>=0) { x = getx(); }\n" " if (x==0) { return; }\n" " return 123 / x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); code = "void f() {\n" " X *x = getx();\n" " if(0) { x = 0; }\n" " else { x->y = 1; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #6239 " int x = 4;\n" " if(1) { x = 0; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 4)); code = "void f() {\n" " int x = 32;\n" " if (x>=32) return;\n" " a[x]=0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 32)); code = "void f() {\n" " int x = 32;\n" " if (x>=32) {\n" " a[x] = 0;\n" // <- should have possible value 32 " return;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 32)); code = "void f() {\n" " int x = 33;\n" " if (x==33) goto fail;\n" " a[x]=0;\n" "fail:\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 33)); code = "void f() {\n" " int x = 32;\n" " if (a==1) { z=x+12; }\n" " if (a==2) { z=x+32; }\n" " z = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 32)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 32)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 32)); code = "void f() {\n" // #5656 - FP " int x = 0;\n" " if (!x) {\n" " x = getx();\n" " }\n" " y = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); code = "void f(int y) {\n" // alias " int x = y;\n" " if (y == 54) {}\n" " else { a = x; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 54)); code = "void f () {\n" " ST * x = g_pST;\n" " if (x->y == 0) {\n" " x = NULL;\n" " return 1;\n" " }\n" " a = x->y;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); code = "void f () {\n" " ST * x = g_pST;\n" " if (x->y == 0) {\n" " x = NULL;\n" " goto label;\n" " }\n" " a = x->y;\n" "label:\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); code = "void f() {\n" // #5752 - FP " int *x = 0;\n" " if (x && *x == 123) {\n" " getx(*x);\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x = 0;\n" " if (!x) {}\n" " else { y = x; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #6118 - FP " int x = 0;\n" " x = x & 0x1;\n" " if (x == 0) { x = 2; }\n" " y = 42 / x;\n" // <- x is 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "void f() {\n" // #6118 - FN " int x = 0;\n" " x = x & 0x1;\n" " if (x == 0) { x += 2; }\n" " y = 42 / x;\n" // <- x is 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 2)); code = "void f(int mode) {\n" " struct ABC *x;\n" "\n" " if (mode) { x = &y; }\n" " else { x = NULL; }\n" "\n" " if (!x) exit(1);\n" "\n" " a = x->a;\n" // <- x can't be 0 "}"; ASSERT_EQUALS(false, testValueOfX(code, 9U, 0)); // multivariables code = "void f(int a) {\n" " int x = a;\n" " if (a!=132) { b = x; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 132)); code = "void f(int a) {\n" " int x = a;\n" " b = x;\n" // <- line 3 " if (a!=132) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 132)); code = "void f() {\n" " int a;\n" " if (n) { a = n; }\n" " else { a = 0; }\n" " int x = a;\n" " if (a > 0) { a = b / x; }\n" // <- line 6 "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); // x is not 0 at line 6 code = "void f(int x1) {\n" // #6086 " int x = x1;\n" " if (x1 >= 3) {\n" " return;\n" " }\n" " a = x;\n" // <- x is not 3 "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 3)); code = "int f(int *x) {\n" // #5980 " if (!x) {\n" " switch (i) {\n" " default:\n" " throw std::runtime_error(msg);\n" " };\n" " }\n" " return *x;\n" // <- x is not 0 "}"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 0)); code = "void f(int a) {\n" // #6826 " int x = a ? a : 87;\n" " if (a && x) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 87)); code = "void f() {\n" " int first=-1, x=0;\n" " do {\n" " if (first >= 0) { a = x; }\n" // <- x is not 0 " first++; x=3;\n" " } while (1);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // pointer/reference to x code = "int f(void) {\n" " int x = 2;\n" " int *px = &x;\n" " for (int i = 0; i < 1; i++) {\n" " *px = 1;\n" " }\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 2)); code = "int f(void) {\n" " int x = 5;\n" " int &rx = x;\n" " for (int i = 0; i < 1; i++) {\n" " rx = 1;\n" " }\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 5)); // break code = "void f() {\n" " for (;;) {\n" " int x = 1;\n" " if (!abc()) {\n" " x = 2;\n" " break;\n" " }\n" " a = x;\n" // <- line 8 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 2)); // x is not 2 at line 8 code = "void f() {\n" " int x;\n" " switch (ab) {\n" " case A: x = 12; break;\n" " case B: x = 34; break;\n" " }\n" " v = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 7U, 12)); ASSERT_EQUALS(true, testValueOfX(code, 7U, 34)); code = "void f() {\n" // #5981 " int x;\n" " switch (ab) {\n" " case A: x = 12; break;\n" " case B: x = 34; break;\n" " }\n" " switch (ab) {\n" " case A: v = x; break;\n" // <- x is not 34 " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 34)); // while/for code = "void f() {\n" // #6138 " ENTRY *x = 0;\n" " while (x = get()) {\n" " set(x->value);\n" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f(const int *buf) {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " break;\n" " }\n" " }\n" " a = x;\n" // <- x can be 0 "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9 code = "void f(const int *buf) {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " ;\n" // <- no break " }\n" " }\n" " a = x;\n" // <- x can't be 0 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 9U, 0)); // x can't be 0 at line 9 code = "void f(const int *buf) {\n" " int x = 0;\n" " while (++i < 10) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " break;\n" " }\n" " }\n" " a = x;\n" // <- x can be 0 "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9 code = "void f(const int a[]) {\n" // #6616 " const int *x = 0;\n" " for (int i = 0; i < 10; i = *x) {\n" // <- x is not 0 " x = a[i];\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // alias code = "void f() {\n" // #7778 " int x = 0;\n" " int *p = &x;\n" " x = 3;\n" " *p = 2;\n" " a = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 3)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 6U, 2)); } void valueFlowAfterCondition() { const char *code; // in if code = "void f(int x) {\n" " if (x == 123) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x != 123) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); // in else code = "void f(int x) {\n" " if (x == 123) {}\n" " else a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x != 123) {}\n" " else a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); // after if code = "void f(int x) {\n" " if (x == 10) {\n" " x++;\n" " }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 10)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 11)); // ! code = "void f(int x) {\n" " if (!x) { a = x; }\n" " else a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "void f(int x, int y) {\n" " if (!(x&&y)) { return; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f(int x) {\n" " if (!x) { { throw new string(); }; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // if (var) code = "void f(int x) {\n" " if (x) { a = x; }\n" // <- x is not 0 " else { b = x; }\n" // <- x is 0 " c = x;\n" // <- x might be 0 "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); // After while code = "void f(int x) {\n" " while (x != 3) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 3)); code = "void f(int x) {\n" " while (11 != (x = dostuff())) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 11)); code = "void f(int x) {\n" " while (11 != (x = dostuff()) && y) {}\n" " a = x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 11)); code = "void f(int x) {\n" " while (x = dostuff()) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(const Token *x) {\n" // #5866 " x = x->next();\n" " while (x) { x = x->next(); }\n" " if (x->str()) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); code = "void f(const Token *x) {\n" " while (0 != (x = x->next)) {}\n" " x->ab = 0;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(const Token* x) {\n" " while (0 != (x = x->next)) {}\n" " if (x->str) {\n" // <- possible value 0 " x = y;\n" // <- this caused some problem " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); // conditional code after if/else/while code = "void f(int x) {\n" " if (x == 2) {}\n" " if (x > 0)\n" " a = x;\n" // <- TODO, x can be 2 " else\n" " b = x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 4U, 2)); ASSERT_EQUALS(false, testValueOfX(code, 6U, 2)); // condition with 2nd variable code = "void f(int x) {\n" " int y = 0;\n" " if (x == 7) { y = 1; }\n" " if (!y)\n" " a = x;\n" // <- x can not be 7 here "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 7)); code = "void f(struct X *x) {\n" " bool b = TRUE;\n" " if(x) { }\n" " else\n" " b = FALSE;\n" " if (b)\n" " abc(x->value);\n" // <- x is not 0 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); // In condition, after && and || code = "void f(int x) {\n" " a = (x != 3 ||\n" " x);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 3)); code = "void f(int x) {\n" " a = (x == 4 &&\n" " x);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 4)); // protected usage with && code = "void f(const Token* x) {\n" " if (x) {}\n" " for (; x && \n" " x->str() != y; x = x->next()) {}\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f(const Token* x) {\n" " if (x) {}\n" " if (x && \n" " x->str() != y) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // return code = "void f(int x) {\n" // #6024 " if (x == 5) {\n" " if (z) return; else return;\n" " }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 5)); code = "void f(int x) {\n" // #6730 " if (x == 5) {\n" " if (z) continue; else throw e;\n" " }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 5)); // TODO: float code = "void f(float x) {\n" " if (x == 0.5) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // aliased variable code = "void f() {\n" " int x = 1;\n" " int *data = &x;\n" " if (!x) {\n" " calc(data);\n" " a = x;\n" // <- x might be changed by calc " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); } void valueFlowForwardCompoundAssign() { const char *code; code = "void f() {\n" " int x = 123;\n" " x += 43;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 166)); ASSERT_EQUALS("2,Assignment 'x=123', assigned value is 123\n" "3,Compound assignment '+=', assigned value is 166\n", getErrorPathForX(code, 4U)); code = "void f() {\n" " int x = 123;\n" " x /= 0;\n" // don't crash when evaluating x/=0 " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); code = "void f() {\n" " float x = 123.45;\n" " x += 67;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123.45F + 67, 0.01F)); } void valueFlowForwardCorrelatedVariables() { const char *code; code = "void f(int x = 0) {\n" " bool zero(x==0);\n" " if (zero) a = x;\n" // <- x is 0 " else b = x;\n" // <- x is not 0 "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); } void valueFlowForwardFunction() { const char *code; code = "class C {\n" "public:\n" " C(int &i);\n" // non-const argument => might be changed "};\n" "int f() {\n" " int x=1;\n" " C c(x);\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 1)); code = "class C {\n" "public:\n" " C(const int &i);\n" // const argument => is not changed "};\n" "int f() {\n" " int x=1;\n" " C c(x);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 8U, 1)); } void valueFlowForwardLambda() { const char *code; code = "void f() {\n" " int x=1;\n" " auto f = [&](){ a=x; }\n" // x is not 1 " x = 2;\n" " f();\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 2)); code = "void f() {\n" " int x=3;\n" " auto f = [&](){ a=x; }\n" // todo: x is 3 " f();\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 3)); } void valueFlowBitAnd() { const char *code; code = "int f(int a) {\n" " int x = a & 0x80;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3U,0)); ASSERT_EQUALS(true, testValueOfX(code,3U,0x80)); code = "int f(int a) {\n" " int x = a & 0x80 ? 1 : 2;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code,3U,0)); ASSERT_EQUALS(false, testValueOfX(code,3U,0x80)); code = "int f() {\n" " int x = (19 - 3) & 15;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3U,0)); ASSERT_EQUALS(false, testValueOfX(code,3U,16)); } void valueFlowSwitchVariable() { const char *code; code = "void f(int x) {\n" " a = x - 1;\n" // <- x can be 14 " switch (x) {\n" " case 14: a=x+2; break;\n" // <- x is 14 " };\n" " a = x;\n" // <- x can be 14 "}"; ASSERT_EQUALS(true, testConditionalValueOfX(code, 2U, 14)); ASSERT_EQUALS(true, testConditionalValueOfX(code, 4U, 14)); ASSERT_EQUALS(true, testConditionalValueOfX(code, 6U, 14)); ValueFlow::Value value1 = valueOfTok(code, "-"); ASSERT_EQUALS(13, value1.intvalue); ASSERT(!value1.isKnown()); ValueFlow::Value value2 = valueOfTok(code, "+"); ASSERT_EQUALS(16, value2.intvalue); ASSERT(value2.isKnown()); } void valueFlowForLoop() { const char *code; code = "void f() {\n" " for (int x = 0; x < 10; x++)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 10)); code = "void f() {\n" " int x;\n" " for (x = 2; x < 1; x++)\n" " a[x] = 0;\n" // <- not 2 " b = x;\n" // 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "void f() {\n" " int x;\n" " for (x = 2; x < 1; ++x)\n" " a[x] = 0;\n" // <- not 2 " b = x;\n" // 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "enum AB {A,B};\n" // enum => handled by valueForLoop2 "void f() {\n" " int x;\n" " for (x = 1; x < B; ++x)\n" " a[x] = 0;\n" // <- not 1 "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 1)); code = "void f(int a) {\n" " for (int x = a; x < 10; x++)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); code = "void f() {\n" " for (int x = 0; x < 10; x = x + 2)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 8)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 10)); code = "void f() {\n" " for (int x = 0; x < 10; x = x / 0)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); // don't crash code = "void f() {\n" " for (int x = 0; x < 10; x++)\n" " x<4 ?\n" " a[x] : 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 9)); code = "void f() {\n" " for (int x = 0; x < 10; x++)\n" " x==0 ?\n" " 0 : a[x];\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #5223 " for (int x = 0; x < 300 && x < 18; x++)\n" " x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 17)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 299)); code = "void f() {\n" " int x;\n" " for (int i = 0; x = bar[i]; i++)\n" " x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " const char abc[] = \"abc\";\n" " int x;\n" " for (x = 0; abc[x] != '\\0'; x++) {}\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 3)); code = "void f() {\n" // #5939 " int x;\n" " for (int x = 0; (x = do_something()) != 0;)\n" " x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x;\n" " for (int x = 0; x < 10 && y = do_something();)\n" " x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x,y;\n" " for (x = 0, y = 0; x < 10, y < 10; x++, y++)\n" // usage of , " x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); code = "void foo(double recoveredX) {\n" " for (double x = 1e-18; x < 1e40; x *= 1.9) {\n" " double relativeError = (x - recoveredX) / x;\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // Ticket #7139 // "<<" in third expression of for code = "void f(void) {\n" " int bit, x;\n" " for (bit = 1, x = 0; bit < 128; bit = bit << 1, x++) {\n" " z = x;\n" // <- known value [0..6] " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 6)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 7)); // && code = "void foo() {\n" " for (int x = 0; x < 10; x++) {\n" " if (x > 1\n" " && x) {}" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 9)); code = "void foo() {\n" " for (int x = 0; x < 10; x++) {\n" " if (x < value\n" " && x) {}" // <- maybe x is not 9 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 9)); // || code = "void foo() {\n" " for (int x = 0; x < 10; x++) {\n" " if (x == 0\n" " || x) {}" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 9)); // After loop code = "void foo() {\n" " int x;\n" " for (x = 0; x < 10; x++) {}\n" " a = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 10)); code = "void foo() {\n" " int x;\n" " for (x = 0; 2 * x < 20; x++) {}\n" " a = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 10)); code = "void foo() {\n" // related with #887 " int x;\n" " for (x = 0; x < 20; x++) {}\n" " a = x++;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 20)); code = "void f() {\n" " int x;\n" " for (x = 0; x < 5; x++) {}\n" " if (x == 5) {\n" " panic();\n" " }\n" " a = x;\n" // <- x can't be 5 "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 5)); code = "void f() {\n" " int x;\n" " for (x = 0; x < 5; x++) {}\n" " if (x < 5) {}\n" " else return;\n" " a = x;\n" // <- x can't be 5 "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 5)); // assert after for loop.. code = "static void f() {\n" " int x;\n" " int ctls[10];\n" " for (x = 0; x <= 10; x++) {\n" " if (cond)\n" " break;\n" " }\n" " assert(x <= 10);\n" " ctls[x] = 123;\n" // <- x can't be 11 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 9U, 11)); // hang code = "void f() {\n" " for(int i = 0; i < 20; i++)\n" " n = (int)(i < 10 || abs(negWander) < abs(negTravel));\n" "}"; testValueOfX(code,0,0); // <- don't hang // conditional code in loop code = "void f(int mask) {\n" // #6000 " for (int x = 10; x < 14; x++) {\n" " int bit = mask & (1 << i);\n" " if (bit) {\n" " if (bit == (1 << 10)) {}\n" " else { a = x; }\n" // <- x is not 10 " }\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 10)); // #7886 - valueFlowForLoop must be called after valueFlowAfterAssign code = "void f() {\n" " int sz = 4;\n" " int x,y;\n" " for(x=0,y=0; x < sz && y < 10; x++)\n" " a = x;\n" // <- max value is 3 "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 3)); } void valueFlowSubFunction() { const char *code; code = "void f1(int x) { return x; }\n" "void f2(int x) {\n" " f1(123);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 1U, 123)); code = "void f1(int x) { return x; }\n" "void f2(int x) {\n" " f1(x);\n" " if (x==0){}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 1U, 0)); code = "void f1(int x) {\n" " if (x == 0) return;\n" " int y = 1234 / x;\n" "}\n" "\n" "void f2() {\n" " f1(0);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f1(int x) {\n" " if (x == 0) return;\n" " int y = x;\n" "}\n" "\n" "void f2() {\n" " f1(x&4);\n" // possible {0,4} "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 4)); code = "void f1(int x) { a=x; }\n" "void f2(int y) { f1(y<123); }\n"; ASSERT_EQUALS(true, testValueOfX(code, 1U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 1U, 1)); code = "void f1(int x) { a=(abc)x; }\n" "void f2(int y) { f1(123); }\n"; ASSERT_EQUALS(true, testValueOfX(code, 1U, 123)); code = "void f1(int x) {\n" " x ?\n" " 1024 / x :\n" " 0; }\n" "void f2() { f1(0); }"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f1(int *x) {\n" " if (x &&\n" " *x) {}\n" "}\n" "void f2() { f1(0); }"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // #5861 - fp with float code = "void f1(float x) {\n" " return 1.0 / x;\n" "}\n" "void f2() { f1(0.5); }"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); code = "void dostuff(int x) {\n" " return x/x;\n" "}\n" "\n" "void test(int x) {\n" " if(x==1) {}\n" " dostuff(x+1);\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 2)); code = "void leaveNotifyEvent(const XCrossingEvent * const) { }\n" "void motionNotifyEvent() {\n" " leaveNotifyEvent(0);\n" "}"; testValueOfX(code, 2U, 2); // No complaint about Token::Match called with varid 0. (#6443) // #6560 - multivariables code = "void f1(int x) {\n" " int a = x && y;\n" " int b = a ? x : 0;\n" "}\n" "void f2() {\n" " f1(0);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "class A\n" "{\n" " void f1(int x) { return x; }\n" " void f2(int x) {\n" " f1(123);\n" " }\n" "};"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "class A\n" "{\n" " virtual void f1(int x) { return x; }\n" " void f2(int x) {\n" " f1(123);\n" " }\n" "};"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void foo(int x, int y) {\n" " if (y == 1) {\n" " a = x;\n" // <- x is not 1 " }\n" "}\n" "\n" "void bar() {\n" " foo(1, 10);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); } void valueFlowFunctionReturn() { const char *code; code = "int f1(int x) {\n" " return x+1;\n" "}\n" "void f2() {\n" " x = 10 - f1(2);\n" "}"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "-").isKnown()); code = "int add(int x, int y) {\n" " return x+y;\n" "}\n" "void f2() {\n" " x = 1 * add(10+1,4);\n" "}"; ASSERT_EQUALS(15, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int one() { return 1; }\n" "void f() { x = 1 * one(); }"; ASSERT_EQUALS(1, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int add(int x, int y) {\n" " return x+y;\n" "}\n" "void f2() {\n" " x = 1 * add(1,add(2,3));\n" "}"; ASSERT_EQUALS(6, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int f(int i, X x) {\n" " if (i)\n" " return g(std::move(x));\n" " g(x);\n" " return 0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MovedVariable)); code = "class A\n" "{\n" " int f1(int x) {\n" " return x+1;\n" " }\n" " void f2() {\n" " x = 10 - f1(2);\n" " }\n" "};"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "-").isKnown()); code = "class A\n" "{\n" " virtual int f1(int x) {\n" " return x+1;\n" " }\n" " void f2() {\n" " x = 10 - f1(2);\n" " }\n" "};"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); ASSERT_EQUALS(false, valueOfTok(code, "-").isKnown()); } void valueFlowFunctionDefaultParameter() { const char *code; code = "class continuous_src_time {\n" " continuous_src_time(std::complex f, double st = 0.0, double et = infinity) {}\n" "};"; testValueOfX(code, 2U, 2); // Don't crash (#6494) } bool isNotKnownValues(const char code[], const char str[]) { const std::list values = tokenValues(code, str); for (std::list::const_iterator it = values.begin(); it != values.end(); ++it) { if (it->isKnown()) return false; } return true; } void knownValue() { const char *code; ValueFlow::Value value; ASSERT(valueOfTok("x = 1;", "1").isKnown()); // after assignment code = "void f() {\n" " int x = 1;\n" " return x + 2;\n" // <- known value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(3, value.intvalue); ASSERT(value.isKnown()); { code = "void f() {\n" " int x = 15;\n" " if (x == 15) { x += 7; }\n" // <- condition is true "}"; value = valueOfTok(code, "=="); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "int f() {\n" " int a = 0, x = 0;\n" " a = index();\n" " if (a != 0)\n" " x = next();\n" " return x + 1;\n" "}\n"; value = valueOfTok(code, "+"); ASSERT(value.isPossible()); } code = "void f() {\n" " int x;\n" " if (ab) { x = 7; }\n" " return x + 2;\n" // <- possible value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(9, value.intvalue); ASSERT(value.isPossible()); code = "void f(int c) {\n" " int x = 0;\n" " if (c) {} else { x++; }\n" " return x + 2;\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "+")); code = "void f() {\n" " int x = 0;\n" " dostuff(&x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " int x = 0;\n" " dostuff(0 ? ptr : &x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " int x = 0;\n" " dostuff(unknown ? ptr : &x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " int x = 0;\n" " fred.dostuff(x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void dostuff(int x);\n" "void f() {\n" " int x = 0;\n" " dostuff(x);\n" " if (x < 0) {}\n" "}\n"; value = valueOfTok(code, "<"); ASSERT_EQUALS(0, value.intvalue); ASSERT(value.isKnown()); code = "void dostuff(int & x);\n" "void f() {\n" " int x = 0;\n" " dostuff(x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void dostuff(const int & x);\n" "void f() {\n" " int x = 0;\n" " dostuff(x);\n" " if (x < 0) {}\n" "}\n"; value = valueOfTok(code, "<"); ASSERT_EQUALS(0, value.intvalue); ASSERT(value.isKnown()); code = "void f() {\n" " int x = 0;\n" " do {\n" " if (x < 0) {}\n" " fred.dostuff(x);\n" " } while (abc);\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "int x;\n" "void f() {\n" " x = 4;\n" " while (1) {\n" " a = x+2;\n" " dostuff();\n" " }\n" "}"; ASSERT(isNotKnownValues(code, "+")); code = "void f() {\n" " int x = 0;\n" " if (y) { dostuff(x); }\n" " if (!x) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " MACRO( v, { if (y) { x++; } } );\n" " if (!x) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (cond) {\n" " x = 1;\n" " break;\n" " }\n" " }\n" " if (!x) {}\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " switch (state) {\n" " case 1:\n" " x = 1;\n" " break;\n" " }\n" " if (!x) {}\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" // #7049 " int x = 0;\n" " switch (a) {\n" " case 1:\n" " x = 1;\n" " case 2:\n" " if (!x) {}\n" // <- possible value " }\n" "}"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " while (!x) {\n" // <- possible value " scanf(\"%d\", &x);\n" " }\n" "}"; value = valueOfTok(code, "!"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isPossible()); code = "void f() {\n" " int x = 0;\n" " do { } while (++x < 12);\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " static int x = 0;\n" " return x + 1;\n" // <- known value "}\n"; value = valueOfTok(code, "+"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "void f() {\n" " int x = 0;\n" "a:\n" " a = x + 1;\n" // <- possible value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isPossible()); // in conditional code code = "void f(int x) {\n" " if (!x) {\n" " a = x+1;\n" // <- known value " }\n" "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "void f(int x) {\n" " if (a && 4==x && y) {\n" " a = x+12;\n" // <- known value " }\n" "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(16, value.intvalue); ASSERT(value.isKnown()); // after condition code = "int f(int x) {\n" " if (x == 4) {}\n" " return x + 1;\n" // <- possible value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(5, value.intvalue); ASSERT(value.isPossible()); code = "int f(int x) {\n" " if (x < 2) {}\n" " else if (x >= 2) {}\n" // <- known value "}"; value = valueOfTok(code, ">="); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "int f(int x) {\n" " if (x < 2) {}\n" " else if (x > 2) {}\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, ">")); // function code = "int f(int x) { return x + 1; }\n" // <- possible value "void a() { f(12); }"; value = valueOfTok(code, "+"); ASSERT_EQUALS(13, value.intvalue); ASSERT(value.isPossible()); // known and possible value code = "void f() {\n" " int x = 1;\n" " int y = 2 + x;\n" // <- known value, don't care about condition " if (x == 2) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); // value of x can be 1 ASSERT_EQUALS(false, testValueOfX(code, 3U, 2)); // value of x can't be 2 // calculation with known result code = "int f(int x) { a = x & 0; }"; // <- & is 0 value = valueOfTok(code, "&"); ASSERT_EQUALS(0, value.intvalue); ASSERT(value.isKnown()); // template parameters are not known code = "template void f() { a = X; }\n" "f<1>();"; value = valueOfTok(code, "1"); ASSERT_EQUALS(1, value.intvalue); ASSERT_EQUALS(false, value.isKnown()); } void valueFlowSizeofForwardDeclaredEnum() { const char *code = "enum E; sz=sizeof(E);"; valueOfTok(code, "="); // Don't crash (#7775) } void valueFlowGlobalVar() { const char *code; code = "int x;\n" "void f() {\n" " x = 4;\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 4)); code = "int x;\n" "void f() {\n" " if (x == 4) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 4)); code = "int x;\n" "void f() {\n" " x = 42;\n" " unknownFunction();\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 42)); } void valueFlowGlobalStaticVar() { const char *code; code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { x=a; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { p = &x; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { x++; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { foo(x); }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); } void valueFlowInlineAssembly() { const char* code = "void f() {\n" " int x = 42;\n" " asm(\"\");\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 42)); } void valueFlowUninit() { const char* code; std::list values; code = "void f() {\n" " int x;\n" " switch (x) {}\n" "}"; values = tokenValues(code, "x )"); ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue()); code = "void f() {\n" " C *c;\n" " if (c->x() == 4) {}\n" "}"; values = tokenValues(code, "c ."); ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue()); code = "void f() {\n" " int **x;\n" " y += 10;\n" " x = dostuff(sizeof(*x)*y);\n" "}"; ASSERT_EQUALS(0U, tokenValues(code, "x )").size()); // #8036 code = "void foo() {\n" " int x;\n" " f(x=3), return x+3;\n" "}"; ASSERT_EQUALS(0U, tokenValues(code, "x +").size()); // #8195 code = "void foo(std::istream &is) {\n" " int x;\n" " if (is >> x) {\n" " a = x;\n" " }\n" "}"; values = tokenValues(code, "x ; }"); ASSERT_EQUALS(true, values.empty()); // return (#8173) code = "int repeat() {\n" " const char *n;\n" " return((n=42) && *n == 'A');\n" "}"; values = tokenValues(code, "n =="); ASSERT_EQUALS(true, values.size() != 1U || !values.front().isUninitValue()); // #8233 code = "void foo() {\n" " int x;\n" " int y = 1;\n" " if (y>1)\n" " x = 1;\n" " else\n" " x = 1;\n" " if (x>1) {}\n" "}"; values = tokenValues(code, "x >"); ASSERT_EQUALS(true, values.size() == 1U && values.front().isIntValue()); } }; REGISTER_TEST(TestValueFlow) cppcheck-1.82/test/testvarid.cpp000066400000000000000000003573651322667425100167470ustar00rootroot00000000000000/* * 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 "platform.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include struct InternalError; class TestVarID : public TestFixture { public: TestVarID() : TestFixture("TestVarID") { } private: void run() { TEST_CASE(varid1); TEST_CASE(varid2); TEST_CASE(varid3); TEST_CASE(varid4); TEST_CASE(varid5); TEST_CASE(varid6); TEST_CASE(varid7); TEST_CASE(varidReturn1); TEST_CASE(varidReturn2); TEST_CASE(varid8); TEST_CASE(varid9); TEST_CASE(varid10); TEST_CASE(varid11); TEST_CASE(varid12); TEST_CASE(varid13); TEST_CASE(varid14); TEST_CASE(varid15); TEST_CASE(varid16); TEST_CASE(varid17); // ticket #1810 TEST_CASE(varid18); TEST_CASE(varid19); TEST_CASE(varid20); TEST_CASE(varid24); TEST_CASE(varid25); TEST_CASE(varid26); // ticket #1967 (list of function pointers) TEST_CASE(varid27); // Ticket #2280 (same name for namespace and variable) TEST_CASE(varid28); // ticket #2630 TEST_CASE(varid29); // ticket #1974 TEST_CASE(varid30); // ticket #2614 TEST_CASE(varid34); // ticket #2825 TEST_CASE(varid35); // function declaration inside function body TEST_CASE(varid36); // ticket #2980 (segmentation fault) TEST_CASE(varid37); // ticket #3092 (varid for 'Bar bar(*this);') TEST_CASE(varid38); // ticket #3272 (varid for 'FOO class C;') TEST_CASE(varid39); // ticket #3279 (varid for 'FOO::BAR const') TEST_CASE(varid40); // ticket #3279 TEST_CASE(varid41); // ticket #3340 (varid for union type) TEST_CASE(varid42); // ticket #3316 (varid for array) TEST_CASE(varid43); TEST_CASE(varid44); TEST_CASE(varid45); // #3466 TEST_CASE(varid46); // struct varname TEST_CASE(varid47); // function parameters TEST_CASE(varid48); // #3785 - return (a*b) TEST_CASE(varid49); // #3799 - void f(std::vector) TEST_CASE(varid50); // #3760 - explicit TEST_CASE(varid51); // don't set varid for template function TEST_CASE(varid52); // Set varid for nested templates TEST_CASE(varid53); // #4172 - Template instantiation: T<&functionName> list[4]; TEST_CASE(varid54); // hang TEST_CASE(varid55); // #5868: Function::addArgument with varid 0 for argument named the same as a typedef TEST_CASE(varid56); // function with a throw() TEST_CASE(varid57); // #6636: new scope by {} TEST_CASE(varid58); // #6638: for loop in for condition TEST_CASE(varid59); // #6696 TEST_CASE(varid60); // #7267 cast '(unsigned x)10' TEST_CASE(varid61); // #4988 inline function TEST_CASE(varid_cpp_keywords_in_c_code); TEST_CASE(varid_cpp_keywords_in_c_code2); // #5373: varid=0 for argument called "delete" TEST_CASE(varidFunctionCall1); TEST_CASE(varidFunctionCall2); TEST_CASE(varidFunctionCall3); TEST_CASE(varidFunctionCall4); // ticket #3280 TEST_CASE(varidFunctionCall5); TEST_CASE(varidStl); TEST_CASE(varidStl2); TEST_CASE(varid_newauto); // not declaration: new const auto(0); TEST_CASE(varid_delete); TEST_CASE(varid_functions); TEST_CASE(varid_sizeof); TEST_CASE(varid_reference_to_containers); TEST_CASE(varid_in_class1); TEST_CASE(varid_in_class2); TEST_CASE(varid_in_class3); // #3092 - shadow variable in member function TEST_CASE(varid_in_class4); // #3271 - public: class C; TEST_CASE(varid_in_class5); // #3584 - std::vector<::FOO::B> b; TEST_CASE(varid_in_class6); // #3755 TEST_CASE(varid_in_class7); // set variable id for struct members TEST_CASE(varid_in_class8); // unknown macro in class TEST_CASE(varid_in_class9); // #4291 - id for variables accessed through 'this' TEST_CASE(varid_in_class10); TEST_CASE(varid_in_class11); // #4277 - anonymous union TEST_CASE(varid_in_class12); // #4637 - method TEST_CASE(varid_in_class13); // #4637 - method TEST_CASE(varid_in_class14); TEST_CASE(varid_in_class15); // #5533 - functions TEST_CASE(varid_in_class16); TEST_CASE(varid_in_class17); // #6056 - no varid for member functions TEST_CASE(varid_in_class18); // #7127 TEST_CASE(varid_in_class19); TEST_CASE(varid_in_class20); // #7267 TEST_CASE(varid_in_class21); // #7788 TEST_CASE(varid_namespace_1); // #7272 TEST_CASE(varid_namespace_2); // #7000 TEST_CASE(varid_initList); TEST_CASE(varid_initListWithBaseTemplate); TEST_CASE(varid_initListWithScope); TEST_CASE(varid_operator); TEST_CASE(varid_throw); TEST_CASE(varid_unknown_macro); // #2638 - unknown macro is not type TEST_CASE(varid_using); // ticket #3648 TEST_CASE(varid_catch); TEST_CASE(varid_functionPrototypeTemplate); TEST_CASE(varid_templatePtr); // #4319 TEST_CASE(varid_templateNamespaceFuncPtr); // #4172 TEST_CASE(varid_templateArray); TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array Y; TEST_CASE(varid_templateUsing); // #5781 #7273 TEST_CASE(varid_cppcast); // #6190 TEST_CASE(varid_variadicFunc); TEST_CASE(varid_typename); // #4644 TEST_CASE(varid_rvalueref); TEST_CASE(varid_arrayFuncPar); // #5294 TEST_CASE(varid_sizeofPassed); // #5295 TEST_CASE(varid_classInFunction); // #5293 TEST_CASE(varid_pointerToArray); // #2645 TEST_CASE(varid_cpp11initialization); // #4344 TEST_CASE(varid_inheritedMembers); // #4101 TEST_CASE(varid_header); // #6386 TEST_CASE(varid_rangeBasedFor); TEST_CASE(varid_structinit); // #6406 TEST_CASE(varid_arrayinit); // #7579 TEST_CASE(varidclass1); TEST_CASE(varidclass2); TEST_CASE(varidclass3); TEST_CASE(varidclass4); TEST_CASE(varidclass5); TEST_CASE(varidclass6); TEST_CASE(varidclass7); TEST_CASE(varidclass8); TEST_CASE(varidclass9); TEST_CASE(varidclass10); // variable declaration below usage TEST_CASE(varidclass11); // variable declaration below usage TEST_CASE(varidclass12); TEST_CASE(varidclass13); TEST_CASE(varidclass14); TEST_CASE(varidclass15); // initializer list TEST_CASE(varidclass16); // #4577 TEST_CASE(varidclass17); // #6073 TEST_CASE(varidclass18); TEST_CASE(varidclass19); // initializer list TEST_CASE(varid_classnameshaddowsvariablename); // #3990 TEST_CASE(varidnamespace1); TEST_CASE(varidnamespace2); TEST_CASE(usingNamespace1); TEST_CASE(usingNamespace2); TEST_CASE(usingNamespace3); } std::string tokenize(const char code[], bool simplify = false, const char filename[] = "test.cpp") { errout.str(""); Settings settings; settings.platform(Settings::Unix64); settings.standards.c = Standards::C89; settings.standards.cpp = Standards::CPP11; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); // result.. return tokenizer.tokens()->stringifyList(true,true,true,true,false); } void varid1() { { const std::string actual = tokenize( "static int i = 1;\n" "void f()\n" "{\n" " int i = 2;\n" " for (int i = 0; i < 10; ++i)\n" " i = 3;\n" " i = 4;\n" "}\n", false, "test.c"); const char expected[] = "1: static int i@1 = 1 ;\n" "2: void f ( )\n" "3: {\n" "4: int i@2 ; i@2 = 2 ;\n" "5: for ( int i@3 = 0 ; i@3 < 10 ; ++ i@3 ) {\n" "6: i@3 = 3 ; }\n" "7: i@2 = 4 ;\n" "8: }\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize( "static int i = 1;\n" "void f()\n" "{\n" " int i = 2;\n" " for (int i = 0; i < 10; ++i)\n" " {\n" " i = 3;\n" " }\n" " i = 4;\n" "}\n", false, "test.c"); const char expected[] = "1: static int i@1 = 1 ;\n" "2: void f ( )\n" "3: {\n" "4: int i@2 ; i@2 = 2 ;\n" "5: for ( int i@3 = 0 ; i@3 < 10 ; ++ i@3 )\n" "6: {\n" "7: i@3 = 3 ;\n" "8: }\n" "9: i@2 = 4 ;\n" "10: }\n"; ASSERT_EQUALS(expected, actual); } } void varid2() { const std::string actual = tokenize( "void f()\n" "{\n" " struct ABC abc;\n" " abc.a = 3;\n" " i = abc.a;\n" "}\n", false, "test.c"); const char expected[] = "1: void f ( )\n" "2: {\n" "3: struct ABC abc@1 ;\n" "4: abc@1 . a@2 = 3 ;\n" "5: i = abc@1 . a@2 ;\n" "6: }\n"; ASSERT_EQUALS(expected, actual); } void varid3() { const std::string actual = tokenize( "static char str[4];\n" "void f()\n" "{\n" " char str[10];\n" " str[0] = 0;\n" "}\n", false, "test.c"); const char expected[] = "1: static char str@1 [ 4 ] ;\n" "2: void f ( )\n" "3: {\n" "4: char str@2 [ 10 ] ;\n" "5: str@2 [ 0 ] = 0 ;\n" "6: }\n"; ASSERT_EQUALS(expected, actual); } void varid4() { const std::string actual = tokenize( "void f(const unsigned int a[])\n" "{\n" " int i = *(a+10);\n" "}\n", false, "test.c"); const char expected[] = "1: void f ( const unsigned int a@1 [ ] )\n" "2: {\n" "3: int i@2 ; i@2 = * ( a@1 + 10 ) ;\n" "4: }\n"; ASSERT_EQUALS(expected, actual); } void varid5() { const std::string actual = tokenize( "void f()\n" "{\n" " int a,b;\n" "}\n", false, "test.c"); const char expected[] = "1: void f ( )\n" "2: {\n" "3: int a@1 ; int b@2 ;\n" "4: }\n"; ASSERT_EQUALS(expected, actual); } void varid6() { const std::string actual = tokenize( "int f(int a, int b)\n" "{\n" " return a+b;\n" "}\n", false, "test.c"); const char expected[] = "1: int f ( int a@1 , int b@2 )\n" "2: {\n" "3: return a@1 + b@2 ;\n" "4: }\n"; ASSERT_EQUALS(expected, actual); } void varid7() { const std::string actual = tokenize( "void func() {\n" " char a[256] = \"test\";\n" " {\n" " char b[256] = \"test\";\n" " }\n" "}\n", false, "test.c"); const char expected[] = "1: void func ( ) {\n" "2: char a@1 [ 256 ] = \"test\" ;\n" "3: {\n" "4: char b@2 [ 256 ] = \"test\" ;\n" "5: }\n" "6: }\n"; ASSERT_EQUALS(expected, actual); } void varidReturn1() { const std::string actual = tokenize( "int f()\n" "{\n" " int a;\n" " return a;\n" "}\n", false, "test.c"); const char expected[] = "1: int f ( )\n" "2: {\n" "3: int a@1 ;\n" "4: return a@1 ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varidReturn2() { const std::string actual = tokenize( "void foo()\n" "{\n" " unsigned long mask = (1UL << size_) - 1;\n" " return (abits_val_ & mask);\n" "}\n", false, "test.c"); const char expected[] = "1: void foo ( )\n" "2: {\n" "3: unsigned long mask@1 ; mask@1 = ( 1UL << size_ ) - 1 ;\n" "4: return ( abits_val_ & mask@1 ) ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varid8() { const std::string actual = tokenize( "void func()\n" "{\n" " std::string str(\"test\");\n" " str.clear();\n" "}\n"); const char expected[] = "1: void func ( )\n" "2: {\n" "3: std :: string str@1 ( \"test\" ) ;\n" "4: str@1 . clear ( ) ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varid9() { const std::string actual = tokenize( "typedef int INT32;\n", false, "test.c"); const char expected[] = "1: ;\n"; ASSERT_EQUALS(expected, actual); } void varid10() { const std::string actual = tokenize( "void foo()\n" "{\n" " int abc;\n" " struct abc abc1;\n" "}", false, "test.c"); const char expected[] = "1: void foo ( )\n" "2: {\n" "3: int abc@1 ;\n" "4: struct abc abc1@2 ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varid11() { const std::string actual = tokenize( "class Foo;\n"); const char expected[] = "1: class Foo ;\n"; ASSERT_EQUALS(expected, actual); } void varid12() { const std::string actual = tokenize( "static void a()\n" "{\n" " class Foo *foo;\n" "}\n"); const char expected[] = "1: static void a ( )\n" "2: {\n" "3: class Foo * foo@1 ;\n" "4: }\n"; ASSERT_EQUALS(expected, actual); } void varid13() { const std::string actual = tokenize( "void f()\n" "{\n" " int a; int b;\n" " a = a;\n" "}\n", false, "test.c"); const char expected[] = "1: void f ( )\n" "2: {\n" "3: int a@1 ; int b@2 ;\n" "4: a@1 = a@1 ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varid14() { // Overloaded operator* const std::string actual = tokenize( "void foo()\n" "{\n" "A a;\n" "B b;\n" "b * a;\n" "}", false, "test.c"); const char expected[] = "1: void foo ( )\n" "2: {\n" "3: A a@1 ;\n" "4: B b@2 ;\n" "5: b@2 * a@1 ;\n" "6: }\n"; ASSERT_EQUALS(expected, actual); } void varid15() { { const std::string actual = tokenize( "struct S {\n" " struct T {\n" " } t;\n" "} s;", false, "test.c"); const char expected[] = "1: struct S {\n" "2: struct T {\n" "3: } ; struct T t@1 ;\n" "4: } ; struct S s@2 ;\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize( "struct S {\n" " struct T {\n" " } t;\n" "};", false, "test.c"); const char expected[] = "1: struct S {\n" "2: struct T {\n" "3: } ; struct T t@1 ;\n" "4: } ;\n"; ASSERT_EQUALS(expected, actual); } } void varid16() { const char code[] ="void foo()\n" "{\n" " int x = 1;\n" " y = (z * x);\n" "}\n"; const char expected[] = "1: void foo ( )\n" "2: {\n" "3: int x@1 ; x@1 = 1 ;\n" "4: y = z * x@1 ;\n" "5: }\n"; ASSERT_EQUALS(expected, tokenize(code, false, "test.c")); } void varid17() { // ticket #1810 const char code[] ="char foo()\n" "{\n" " char c('c');\n" " return c;\n" "}\n"; const char expected[] = "1: char foo ( )\n" "2: {\n" "3: char c@1 ( 'c' ) ;\n" "4: return c@1 ;\n" "5: }\n"; ASSERT_EQUALS(expected, tokenize(code, false, "test.c")); } void varid18() { const char code[] ="char foo(char c)\n" "{\n" " bar::c = c;\n" "}\n"; const char expected[] = "1: char foo ( char c@1 )\n" "2: {\n" "3: bar :: c = c@1 ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid19() { const char code[] ="void foo()\n" "{\n" " std::pair, int> x;\n" "}\n"; const char expected[] = "1: void foo ( )\n" "2: {\n" "3: std :: pair < std :: vector < double > , int > x@1 ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid20() { const char code[] ="void foo()\n" "{\n" " pair, vector > x;\n" "}\n"; const char expected[] = "1: void foo ( )\n" "2: {\n" "3: pair < vector < int > , vector < double > > x@1 ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid24() { const char code[] ="class foo()\n" "{\n" "public:\n" " ;\n" "private:\n" " static int i;\n" "};\n"; const char expected[] = "1: class foo ( )\n" "2: {\n" "3: public:\n" "4: ;\n" "5: private:\n" "6: static int i@1 ;\n" "7: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid25() { const char code[] ="class foo()\n" "{\n" "public:\n" " ;\n" "private:\n" " mutable int i;\n" "};\n"; const char expected[] = "1: class foo ( )\n" "2: {\n" "3: public:\n" "4: ;\n" "5: private:\n" "6: mutable int i@1 ;\n" "7: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid26() { const char code[] ="list functions;\n"; const char expected[] = "1: list < int ( * ) ( ) > functions@1 ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid27() { const char code[] ="int fooled_ya;\n" "fooled_ya::iterator iter;\n"; const char expected[] = "1: int fooled_ya@1 ;\n" "2: fooled_ya :: iterator iter@2 ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid28() { // ticket #2630 (segmentation fault) ASSERT_THROW(tokenize("template \n"), InternalError); } void varid29() { const char code[] ="class A {\n" " B,1> b;\n" "};\n"; const char expected[] = "1: class A {\n" "2: B < C < 1 > , 1 > b@1 ;\n" "3: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid30() { // ticket #2614 const char code1[] = "void f(EventPtr *eventP, ActionPtr **actionsP)\n" "{\n" " EventPtr event = *eventP;\n" " *actionsP = &event->actions;\n" "}\n"; const char expected1[] = "1: void f ( EventPtr * eventP@1 , ActionPtr * * actionsP@2 )\n" "2: {\n" "3: EventPtr event@3 ; event@3 = * eventP@1 ;\n" "4: * actionsP@2 = & event@3 . actions@4 ;\n" "5: }\n"; ASSERT_EQUALS(expected1, tokenize(code1, false, "test.c")); const char code2[] = "void f(int b, int c) {\n" " x(a*b*c,10);\n" "}\n"; const char expected2[] = "1: void f ( int b@1 , int c@2 ) {\n" "2: x ( a * b@1 * c@2 , 10 ) ;\n" "3: }\n"; ASSERT_EQUALS(expected2, tokenize(code2, false, "test.c")); const char code3[] = "class Nullpointer : public ExecutionPath\n" " {\n" " Nullpointer(Check *c, const unsigned int id, const std::string &name)\n" " : ExecutionPath(c, id)\n" " {\n" " }\n" "}\n"; const char expected3[] = "1: class Nullpointer : public ExecutionPath\n" "2: {\n" "3: Nullpointer ( Check * c@1 , const unsigned int id@2 , const std :: string & name@3 )\n" "4: : ExecutionPath ( c@1 , id@2 )\n" "5: {\n" "6: }\n" "7: }\n"; ASSERT_EQUALS(expected3, tokenize(code3)); } void varid34() { // ticket #2825 const char code[] ="class Fred : public B1, public B2\n" "{\n" "public:\n" " Fred() { a = 0; }\n" "private:\n" " int a;\n" "};\n"; const char expected[] = "1: class Fred : public B1 , public B2\n" "2: {\n" "3: public:\n" "4: Fred ( ) { a@1 = 0 ; }\n" "5: private:\n" "6: int a@1 ;\n" "7: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); ASSERT_EQUALS("", errout.str()); } void varid35() { // function declaration inside function body // #2937 const char code[] ="int foo() {\n" " int f(x);\n" " return f;\n" "}\n"; const char expected[] = "1: int foo ( ) {\n" "2: int f@1 ( x ) ;\n" "3: return f@1 ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code)); // #4627 const char code2[] = "void f() {\n" " int *p;\n" " void bar(int *p);\n" "}"; const char expected2[] = "1: void f ( ) {\n" "2: int * p@1 ;\n" "3: void bar ( int * p ) ;\n" "4: }\n"; ASSERT_EQUALS(expected2, tokenize(code2)); // #7740 const char code3[] = "Float f(float scale) {\n" " return Float(val * scale);\n" "}\n"; const char expected3[] = "1: Float f ( float scale@1 ) {\n" "2: return Float ( val * scale@1 ) ;\n" "3: }\n"; ASSERT_EQUALS(expected3, tokenize(code3)); } void varid36() { // ticket #2980 (segmentation fault) const char code[] ="#elif A\n" "A,a) const char code[] ="void f(std::vector)"; ASSERT_EQUALS("1: void f ( std :: vector < int > )\n", tokenize(code, false, "test.cpp")); } void varid50() { // #3760 - explicit const char code[] ="class A { explicit A(const A&); };"; ASSERT_EQUALS("1: class A { explicit A ( const A & ) ; } ;\n", tokenize(code, false, "test.cpp")); } void varid51() { // don't set varid on template function const char code[] ="T t; t.x<0>();"; ASSERT_EQUALS("1: T t@1 ; t@1 . x < 0 > ( ) ;\n", tokenize(code, false, "test.cpp")); } void varid52() { const char code[] ="A::D> e;\n" "B< C<> > b[10];\n" "B> c[10];"; ASSERT_EQUALS("1: A < B < C > :: D > e@1 ;\n" "2: B < C < > > b@2 [ 10 ] ;\n" "3: B < C < > > c@3 [ 10 ] ;\n", tokenize(code, false, "test.cpp")); } void varid53() { // #4172 - Template instantiation: T<&functionName> list[4]; ASSERT_EQUALS("1: A < & f > list@1 [ 4 ] ;\n", tokenize("A<&f> list[4];", false, "test.cpp")); } void varid54() { // hang // Original source code: libgc tokenize("STATIC ptr_t GC_approx_sp(void) { word sp; sp = (word)&sp; return((ptr_t)sp); }",true); } void varid55() { // Ticket #5868 const char code[] = "typedef struct foo {} foo; " "void bar1(struct foo foo) {} " "void baz1(foo foo) {} " "void bar2(struct foo& foo) {} " "void baz2(foo& foo) {} " "void bar3(struct foo* foo) {} " "void baz3(foo* foo) {}"; const char expected[] = "1: " "struct foo { } ; " "void bar1 ( struct foo foo@1 ) { } " "void baz1 ( struct foo foo@2 ) { } " "void bar2 ( struct foo & foo@3 ) { } " "void baz2 ( struct foo & foo@4 ) { } " "void bar3 ( struct foo * foo@5 ) { } " "void baz3 ( struct foo * foo@6 ) { }\n"; ASSERT_EQUALS(expected, tokenize(code, false, "test.cpp")); } void varid56() { // Ticket #6548 - function with a throw() const char code1[] = "void fred(int x) throw() {}" "void wilma() { x++; }"; const char expected1[] = "1: " "void fred ( int x@1 ) throw ( ) { } " "void wilma ( ) { x ++ ; }\n"; ASSERT_EQUALS(expected1, tokenize(code1, false, "test.cpp")); const char code2[] = "void fred(int x) const throw(EXCEPT) {}" "void wilma() { x++; }"; const char expected2[] = "1: " "void fred ( int x@1 ) const throw ( EXCEPT ) { } " "void wilma ( ) { x ++ ; }\n"; ASSERT_EQUALS(expected2, tokenize(code2, false, "test.cpp")); const char code3[] = "void fred(int x) throw() ABCD {}" "void wilma() { x++; }"; const char expected3[] = "1: " "void fred ( int x@1 ) throw ( ) { } " "void wilma ( ) { x ++ ; }\n"; ASSERT_EQUALS(expected3, tokenize(code3, false, "test.cpp")); const char code4[] = "void fred(int x) noexcept() {}" "void wilma() { x++; }"; const char expected4[] = "1: " "void fred ( int x@1 ) noexcept ( ) { } " "void wilma ( ) { x ++ ; }\n"; ASSERT_EQUALS(expected4, tokenize(code4, false, "test.cpp")); const char code5[] = "void fred(int x) noexcept {}" "void wilma() { x++; }"; const char expected5[] = "1: " "void fred ( int x@1 ) noexcept ( true ) { } " "void wilma ( ) { x ++ ; }\n"; ASSERT_EQUALS(expected5, tokenize(code5, false, "test.cpp")); const char code6[] = "void fred(int x) noexcept ( false ) {}" "void wilma() { x++; }"; const char expected6[] = "1: " "void fred ( int x@1 ) noexcept ( false ) { } " "void wilma ( ) { x ++ ; }\n"; ASSERT_EQUALS(expected6, tokenize(code6, false, "test.cpp")); } void varid57() { // #6636: new scope by {} const char code1[] = "void SmoothPath() {\n" " {\n" // new scope " float dfx = (p2p0.x > 0.0f)?\n" " ((n0->xmax() * SQUARE_SIZE) - p0.x):\n" " ((n0->xmin() * SQUARE_SIZE) - p0.x);\n" " float tx = dfx / dx;\n" " if (hEdge) {\n" " }\n" " if (vEdge) {\n" " pi.z = tx;\n" " }\n" " }\n" "}\n"; const char expected1[] = "1: void SmoothPath ( ) {\n" "2:\n" "3: float dfx@1 ; dfx@1 = ( p2p0 . x > 0.0f ) ?\n" "4: ( ( n0 . xmax ( ) * SQUARE_SIZE ) - p0 . x ) :\n" "5: ( ( n0 . xmin ( ) * SQUARE_SIZE ) - p0 . x ) ;\n" "6: float tx@2 ; tx@2 = dfx@1 / dx ;\n" "7: if ( hEdge ) {\n" "8: }\n" "9: if ( vEdge ) {\n" "10: pi . z = tx@2 ;\n" "11: }\n" "12:\n" "13: }\n"; ASSERT_EQUALS(expected1, tokenize(code1, false, "test.cpp")); } void varid58() { // #6638: for loop in for condition const char code1[] = "void f() {\n" " for (int i;\n" " ({for(int i;i;++i){i++;}i++;}),i;\n" " ({for(int i;i;++i){i++;}i++;}),i++) {\n" " i++;\n" " }\n" "}\n"; const char expected1[] = "1: void f ( ) {\n" "2: for ( int i@1 ;\n" "3: ( { for ( int i@2 ; i@2 ; ++ i@2 ) { i@2 ++ ; } i@1 ++ ; } ) , i@1 ;\n" "4: ( { for ( int i@3 ; i@3 ; ++ i@3 ) { i@3 ++ ; } i@1 ++ ; } ) , i@1 ++ ) {\n" "5: i@1 ++ ;\n" "6: }\n" "7: }\n"; ASSERT_EQUALS(expected1, tokenize(code1, false, "test.cpp")); } void varid59() { // #6696 const char code[] = "class DLLSYM B;\n" "struct B {\n" " ~B() {}\n" "};"; const char expected[] = "1: class DLLSYM B@1 ;\n" // In this line, we cannot really do better... "2: struct B {\n" "3: ~ B@1 ( ) { }\n" // ...but here we could "4: } ;\n"; const char wanted[] = "1: class DLLSYM B@1 ;\n" "2: struct B {\n" "3: ~ B ( ) { }\n" "4: } ;\n";; TODO_ASSERT_EQUALS(wanted, expected, tokenize(code, false, "test.cpp")); } void varid60() { // #7267 - cast ASSERT_EQUALS("1: a = ( x y ) 10 ;\n", tokenize("a=(x y)10;", false)); } void varid61() { const char code[] = "void foo(int b) {\n" " void bar(int a, int b) {}\n" "}"; const char expected[] = "1: void foo ( int b@1 ) {\n" "2: void bar ( int a@2 , int b@3 ) { }\n" "3: }\n"; ASSERT_EQUALS(expected, tokenize(code, false)); } void varid_cpp_keywords_in_c_code() { const char code[] = "void f() {\n" " delete d;\n" " throw t;\n" "}"; const char expected[] = "1: void f ( ) {\n" "2: delete d@1 ;\n" "3: throw t@2 ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code,false,"test.c")); } void varid_cpp_keywords_in_c_code2() { // #5373 const char code[] = "int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, " "unsigned long bits, int wake, int delete, struct extent_state **cached_state, " "gfp_t mask) {\n" " struct extent_state *state;\n" "}" "int clear_extent_dirty() {\n" " return clear_extent_bit(tree, start, end, EXTENT_DIRTY | EXTENT_DELALLOC | " " EXTENT_DO_ACCOUNTING, 0, 0, NULL, mask);\n" "}"; tokenize(code, false, "test.c"); } void varidFunctionCall1() { const char code[] ="void f() {\n" " int x;\n" " x = a(y*x,10);\n" "}"; const char expected[] = "1: void f ( ) {\n" "2: int x@1 ;\n" "3: x@1 = a ( y * x@1 , 10 ) ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code, false, "test.c")); } void varidFunctionCall2() { // #2491 const char code[] ="void f(int b) {\n" " x(a*b,10);\n" "}"; const std::string expected1("1: void f ( int b@1 ) {\n" "2: x ( a * b"); const std::string expected2(" , 10 ) ;\n" "3: }\n"); ASSERT_EQUALS(expected1+"@1"+expected2, tokenize(code,false,"test.c")); } void varidFunctionCall3() { // Ticket #2339 const char code[] ="void f() {\n" " int a = 0;\n" " int b = c - (foo::bar * a);\n" "}"; const char expected[] = "1: void f ( ) {\n" "2: int a@1 ; a@1 = 0 ;\n" "3: int b@2 ; b@2 = c - ( foo :: bar * a@1 ) ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidFunctionCall4() { // Ticket #3280 const char code1[] = "void f() { int x; fun(a,b*x); }"; ASSERT_EQUALS("1: void f ( ) { int x@1 ; fun ( a , b * x@1 ) ; }\n", tokenize(code1, false, "test.c")); const char code2[] = "void f(int a) { int x; fun(a,b*x); }"; ASSERT_EQUALS("1: void f ( int a@1 ) { int x@2 ; fun ( a@1 , b * x@2 ) ; }\n", tokenize(code2, false, "test.c")); } void varidFunctionCall5() { const char code[] = "void foo() { (f(x[2]))(x[2]); }"; ASSERT_EQUALS("1: void foo ( ) { f ( x [ 2 ] ) ( x [ 2 ] ) ; }\n", tokenize(code, false, "test.c")); } void varidStl() { const std::string actual = tokenize( "list ints;\n" "list::iterator it;\n" "std::vector dirs;\n" "std::map coords;\n" "std::tr1::unordered_map xy;\n" "std::list tokens;\n" "static std::vector ex1;\n" "extern std::vector ex2;\n" "std::map m;\n" ); const char expected[] = "1: list < int > ints@1 ;\n" "2: list < int > :: iterator it@2 ;\n" "3: std :: vector < std :: string > dirs@3 ;\n" "4: std :: map < int , int > coords@4 ;\n" "5: std :: unordered_map < int , int > xy@5 ;\n" "6: std :: list < boost :: wave :: token_id > tokens@6 ;\n" "7: static std :: vector < CvsProcess * > ex1@7 ;\n" "8: extern std :: vector < CvsProcess * > ex2@8 ;\n" "9: std :: map < int , 1 > m@9 ;\n"; ASSERT_EQUALS(expected, actual); } void varidStl2() { const std::string actual = tokenize("std::bitset(2)> x;"); const char expected[] = "1: std :: bitset < static_cast < int > ( 2 ) > x@1 ;\n"; ASSERT_EQUALS(expected, actual); } void varid_newauto() { ASSERT_EQUALS("1: void f ( ) { const new auto ( 0 ) ; }\n", tokenize("void f(){new const auto(0);}")); } void varid_delete() { const std::string actual = tokenize( "void f()\n" "{\n" " int *a;\n" " delete a;\n" "}\n"); const char expected[] = "1: void f ( )\n" "2: {\n" "3: int * a@1 ;\n" "4: delete a@1 ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varid_functions() { { const std::string actual = tokenize( "void f();\n" "void f(){}\n", false, "test.c"); const char expected[] = "1: void f ( ) ;\n" "2: void f ( ) { }\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize( "A f(3);\n" "A f2(true);\n" "A g();\n" "A e(int c);\n", false, "test.c"); const char expected[] = "1: A f@1 ( 3 ) ;\n" "2: A f2@2 ( true ) ;\n" "3: A g ( ) ;\n" "4: A e ( int c@3 ) ;\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize( "void f1(int &p)\n" "{\n" " p = 0;\n" "}\n" "void f2(std::string &str)\n" "{\n" " str.clear();\n" "}\n" "void f3(const std::string &s)\n" "{\n" " s.size();\n" "}\n"); const char expected[] = "1: void f1 ( int & p@1 )\n" "2: {\n" "3: p@1 = 0 ;\n" "4: }\n" "5: void f2 ( std :: string & str@2 )\n" "6: {\n" "7: str@2 . clear ( ) ;\n" "8: }\n" "9: void f3 ( const std :: string & s@3 )\n" "10: {\n" "11: s@3 . size ( ) ;\n" "12: }\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize("void f(struct foobar);", false, "test.c"); const char expected[] = "1: void f ( struct foobar ) ;\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize("bool f(X x, int=3);", false, "test.cpp"); const char expected[] = "1: bool f ( X x@1 , int = 3 ) ;\n"; ASSERT_EQUALS(expected, actual); } } void varid_sizeof() { const char code[] = "x = sizeof(a*b);"; const char expected[] = "1: x = sizeof ( a * b ) ;\n"; ASSERT_EQUALS(expected, tokenize(code,false,"test.c")); } void varid_reference_to_containers() { const std::string actual = tokenize( "void f()\n" "{\n" " std::vector b;\n" " std::vector &a = b;\n" " std::vector *c = &b;\n" "}\n"); const char expected[] = "1: void f ( )\n" "2: {\n" "3: std :: vector < int > b@1 ;\n" "4: std :: vector < int > & a@2 = b@1 ;\n" "5: std :: vector < int > * c@3 ; c@3 = & b@1 ;\n" "6: }\n"; ASSERT_EQUALS(expected, actual); } void varid_in_class1() { { const std::string actual = tokenize( "class Foo\n" "{\n" "public:\n" " std::string name1;\n" " std::string name2;\n" "};\n"); const char expected[] = "1: class Foo\n" "2: {\n" "3: public:\n" "4: std :: string name1@1 ;\n" "5: std :: string name2@2 ;\n" "6: } ;\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize( "class foo\n" "{\n" "public:\n" " void do_something(const int x, const int y);\n" " void bar();\n" "};\n" "\n" "void foo::bar()\n" "{\n" " POINT pOutput = { 0 , 0 };\n" " int x = pOutput.x;\n" " int y = pOutput.y;\n" "}\n"); const char expected[] = "1: class foo\n" "2: {\n" "3: public:\n" "4: void do_something ( const int x@1 , const int y@2 ) ;\n" "5: void bar ( ) ;\n" "6: } ;\n" "7:\n" "8: void foo :: bar ( )\n" "9: {\n" "10: POINT pOutput@3 ; pOutput@3 = { 0 , 0 } ;\n" "11: int x@4 ; x@4 = pOutput@3 . x@5 ;\n" "12: int y@6 ; y@6 = pOutput@3 . y@7 ;\n" "13: }\n"; ASSERT_EQUALS(expected, actual); } } void varid_in_class2() { const std::string actual = tokenize( "struct Foo {\n" " int x;\n" "};\n" "\n" "struct Bar {\n" " Foo foo;\n" " int x;\n" " void f();\n" "};\n" "\n" "void Bar::f()\n" "{\n" " foo.x = x;\n" "}\n"); const char expected[] = "1: struct Foo {\n" "2: int x@1 ;\n" "3: } ;\n" "4:\n" "5: struct Bar {\n" "6: Foo foo@2 ;\n" "7: int x@3 ;\n" "8: void f ( ) ;\n" "9: } ;\n" "10:\n" "11: void Bar :: f ( )\n" "12: {\n" "13: foo@2 . x@4 = x@3 ;\n" "14: }\n"; ASSERT_EQUALS(expected, actual); } void varid_in_class3() { const char code[] = "class Foo {\n" " void blah() {\n" " Bar x(*this);\n" // <- .. " }\n" " int x;\n" // <- .. don't assign same varid "};"; ASSERT_EQUALS("1: class Foo {\n" "2: void blah ( ) {\n" "3: Bar x@1 ( * this ) ;\n" "4: }\n" "5: int x@2 ;\n" "6: } ;\n", tokenize(code)); } void varid_in_class4() { const char code[] = "class Foo {\n" "public: class C;\n" "};"; ASSERT_EQUALS("1: class Foo {\n" "2: public: class C ;\n" "3: } ;\n", tokenize(code)); } void varid_in_class5() { const char code[] = "struct Foo {\n" " std::vector<::X> v;\n" "}"; ASSERT_EQUALS("1: struct Foo {\n" "2: std :: vector < :: X > v@1 ;\n" "3: }\n", tokenize(code)); } void varid_in_class6() { const char code[] = "class A {\n" " void f(const char *str) const {\n" " std::stringstream sst;\n" " sst.str();\n" " }\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: void f ( const char * str@1 ) const {\n" "3: std :: stringstream sst@2 ;\n" "4: sst@2 . str ( ) ;\n" "5: }\n" "6: } ;\n", tokenize(code)); } void varid_in_class7() { const char code[] = "class A {\n" " void f() {\n" " abc.a = 0;\n" " }\n" " struct ABC abc;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: void f ( ) {\n" "3: abc@1 . a@2 = 0 ;\n" "4: }\n" "5: struct ABC abc@1 ;\n" "6: } ;\n", tokenize(code)); } void varid_in_class8() { // #3776 - unknown macro const char code[] = "class A {\n" " UNKNOWN_MACRO(A)\n" "private:\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: UNKNOWN_MACRO ( A )\n" "3: private:\n" "4: int x@1 ;\n" "5: } ;\n", tokenize(code)); } void varid_in_class9() { // #4291 - id for variables accessed through 'this' const char code1[] = "class A {\n" " int var;\n" "public:\n" " void setVar();\n" "};\n" "void A::setVar() {\n" " this->var = var;\n" "}"; ASSERT_EQUALS("1: class A {\n" "2: int var@1 ;\n" "3: public:\n" "4: void setVar ( ) ;\n" "5: } ;\n" "6: void A :: setVar ( ) {\n" "7: this . var@1 = var@1 ;\n" "8: }\n", tokenize(code1)); const char code2[] = "class Foo : public FooBase {\n" " void Clone(FooBase& g);\n" " short m_bar;\n" "};\n" "void Foo::Clone(FooBase& g) {\n" " g->m_bar = m_bar;\n" "}"; ASSERT_EQUALS("1: class Foo : public FooBase {\n" "2: void Clone ( FooBase & g@1 ) ;\n" "3: short m_bar@2 ;\n" "4: } ;\n" "5: void Foo :: Clone ( FooBase & g@3 ) {\n" "6: g@3 . m_bar@4 = m_bar@2 ;\n" "7: }\n", tokenize(code2)); // #4311 } void varid_in_class10() { const char code[] = "class Foo : public FooBase {\n" " void Clone(FooBase& g);\n" " short m_bar;\n" "};\n" "void Foo::Clone(FooBase& g) {\n" " ((FooBase)g)->m_bar = m_bar;\n" "}"; TODO_ASSERT_EQUALS("1: class Foo : public FooBase {\n" "2: void Clone ( FooBase & g@1 ) ;\n" "3: short m_bar@2 ;\n" "4: } ;\n" "5: void Foo :: Clone ( FooBase & g@3 ) {\n" "6: ( ( FooBase ) g@3 ) . m_bar@4 = m_bar@2 ;\n" "7: }\n", "1: class Foo : public FooBase {\n" "2: void Clone ( FooBase & g@1 ) ;\n" "3: short m_bar@2 ;\n" "4: } ;\n" "5: void Foo :: Clone ( FooBase & g@3 ) {\n" "6: ( ( FooBase ) g@3 ) . m_bar = m_bar@2 ;\n" "7: }\n", tokenize(code)); } void varid_in_class11() { // #4277 - anonymous union const char code1[] = "class Foo {\n" " union { float a; int b; };\n" " void f() { a=0; }\n" "};"; ASSERT_EQUALS("1: class Foo {\n" "2: union { float a@1 ; int b@2 ; } ;\n" "3: void f ( ) { a@1 = 0 ; }\n" "4: } ;\n", tokenize(code1)); const char code2[] = "class Foo {\n" " void f() { a=0; }\n" " union { float a; int b; };\n" "};"; ASSERT_EQUALS("1: class Foo {\n" "2: void f ( ) { a@1 = 0 ; }\n" "3: union { float a@1 ; int b@2 ; } ;\n" "4: } ;\n", tokenize(code2)); const char code3[] = "void f() {\n" " union {\n" " struct {\n" " char a, b, c, d;\n" " };\n" " int abcd;\n" " };\n" " g(abcd);\n" " h(a, b, c, d);\n" "}"; ASSERT_EQUALS("1: void f ( ) {\n" "2: union {\n" "3: struct {\n" "4: char a@1 ; char b@2 ; char c@3 ; char d@4 ;\n" "5: } ;\n" "6: int abcd@5 ;\n" "7: } ;\n" "8: g ( abcd@5 ) ;\n" "9: h ( a@1 , b@2 , c@3 , d@4 ) ;\n" "10: }\n", tokenize(code3)); // #7444 const char code4[] = "class Foo {\n" " void f(float a) { this->a = a; }\n" " union { float a; int b; };\n" "};"; ASSERT_EQUALS("1: class Foo {\n" "2: void f ( float a@1 ) { this . a@2 = a@1 ; }\n" "3: union { float a@2 ; int b@3 ; } ;\n" "4: } ;\n", tokenize(code4)); } void varid_in_class12() { // #4637 - method const char code[] = "class Foo {\n" "private:\n" " void f(void);\n" "};"; ASSERT_EQUALS("1: class Foo {\n" "2: private:\n" "3: void f ( ) ;\n" "4: } ;\n", tokenize(code)); } void varid_in_class13() { const char code1[] = "struct a { char typename; };"; ASSERT_EQUALS("1: struct a { char typename@1 ; } ;\n", tokenize(code1, false, "test.c")); ASSERT_EQUALS("1: struct a { char typename ; } ;\n", // not valid C++ code tokenize(code1, false, "test.cpp")); const char code2[] = "struct a { char typename[2]; };"; ASSERT_EQUALS("1: struct a { char typename@1 [ 2 ] ; } ;\n", tokenize(code2, false, "test.c")); ASSERT_EQUALS("1: struct a { char typename [ 2 ] ; } ;\n", // not valid C++ code tokenize(code2, false, "test.cpp")); } void varid_in_class14() { const char code[] = "class Tokenizer { TokenList list; };\n" "\n" "void Tokenizer::f() {\n" " std::list x;\n" // <- not member variable " list.do_something();\n" // <- member variable " Tokenizer::list.do_something();\n" // <- redundant scope info "}\n"; ASSERT_EQUALS("1: class Tokenizer { TokenList list@1 ; } ;\n" "2:\n" "3: void Tokenizer :: f ( ) {\n" "4: std :: list < int > x@2 ;\n" "5: list@1 . do_something ( ) ;\n" "6: Tokenizer :: list@1 . do_something ( ) ;\n" "7: }\n", tokenize(code, false, "test.cpp")); } void varid_in_class15() { // #5533 - functions const char code[] = "class Fred {\n" " void x(int a) const;\n" " void y() { a=0; }\n" // <- unknown variable "}\n"; ASSERT_EQUALS("1: class Fred {\n" "2: void x ( int a@1 ) const ;\n" "3: void y ( ) { a = 0 ; }\n" "4: }\n", tokenize(code, false, "test.cpp")); } void varid_in_class16() { // Set varId for inline member functions { const char code[] = "class Fred {\n" " int x;\n" " void foo(int x) { this->x = x; }\n" "};\n"; ASSERT_EQUALS("1: class Fred {\n" "2: int x@1 ;\n" "3: void foo ( int x@2 ) { this . x@1 = x@2 ; }\n" "4: } ;\n", tokenize(code, false, "test.cpp")); } { const char code[] = "class Fred {\n" " void foo(int x) { this->x = x; }\n" " int x;\n" "};\n"; ASSERT_EQUALS("1: class Fred {\n" "2: void foo ( int x@1 ) { this . x@2 = x@1 ; }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code, false, "test.cpp")); } { const char code[] = "class Fred {\n" " void foo(int x) { (*this).x = x; }\n" " int x;\n" "};\n"; ASSERT_EQUALS("1: class Fred {\n" "2: void foo ( int x@1 ) { ( * this ) . x@2 = x@1 ; }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code, false, "test.cpp")); } } void varid_in_class17() { // #6056 - Set no varid for member functions const char code1[] = "class Fred {\n" " int method_with_internal(X&);\n" " int method_with_internal(X*);\n" " int method_with_internal(int&);\n" " int method_with_internal(A* b, X&);\n" " int method_with_internal(X&, A* b);\n" " int method_with_internal(const B &, int);\n" " void Set(BAR);\n" " FOO Set(BAR);\n" " int method_with_class(B b);\n" " bool function(std::map & m);\n" "};"; ASSERT_EQUALS("1: class Fred {\n" "2: int method_with_internal ( X & ) ;\n" "3: int method_with_internal ( X * ) ;\n" "4: int method_with_internal ( int & ) ;\n" "5: int method_with_internal ( A * b@1 , X & ) ;\n" "6: int method_with_internal ( X & , A * b@2 ) ;\n" "7: int method_with_internal ( const B & , int ) ;\n" "8: void Set ( BAR ) ;\n" "9: FOO Set ( BAR ) ;\n" "10: int method_with_class ( B < B > b@3 ) ;\n" "11: bool function ( std :: map < int , int , MYless > & m@4 ) ;\n" "12: } ;\n", tokenize(code1, false, "test.cpp")); const char code2[] = "int i;\n" "SomeType someVar1(i, i);\n" "SomeType someVar2(j, j);\n" "SomeType someVar3(j, 1);\n" "SomeType someVar4(new bar);"; ASSERT_EQUALS("1: int i@1 ;\n" "2: SomeType someVar1@2 ( i@1 , i@1 ) ;\n" "3: SomeType someVar2 ( j , j ) ;\n" // This one could be a function "4: SomeType someVar3@3 ( j , 1 ) ;\n" "5: SomeType someVar4@4 ( new bar ) ;\n", tokenize(code2, false, "test.cpp")); } void varid_in_class18() { const char code[] = "class A {\n" " class B;\n" "};\n" "class A::B {\n" " B();\n" " int* i;\n" "};\n" "A::B::B() :\n" " i(0)\n" "{}"; ASSERT_EQUALS("1: class A {\n" "2: class B ;\n" "3: } ;\n" "4: class A :: B {\n" "5: B ( ) ;\n" "6: int * i@1 ;\n" "7: } ;\n" "8: A :: B :: B ( ) :\n" "9: i@1 ( 0 )\n" "10: { }\n", tokenize(code, false, "test.cpp")); } void varid_in_class19() { const char code[] = "class Fred {\n" " char *str1;\n" " ~Fred();\n" "};\n" "Fred::~Fred() {\n" " free(str1);\n" "}"; ASSERT_EQUALS("1: class Fred {\n" "2: char * str1@1 ;\n" "3: ~ Fred ( ) ;\n" "4: } ;\n" "5: Fred :: ~ Fred ( ) {\n" "6: free ( str1@1 ) ;\n" "7: }\n", tokenize(code, false, "test.cpp")); } void varid_in_class20() { const char code[] = "template class cacheEntry {\n" "protected:\n" " int m_key;\n" "public:\n" " cacheEntry();\n" "};\n" "\n" "template cacheEntry::cacheEntry() : m_key() {}"; ASSERT_EQUALS("1: template < class C > class cacheEntry {\n" "2: protected:\n" "3: int m_key@1 ;\n" "4: public:\n" "5: cacheEntry ( ) ;\n" "6: } ;\n" "7:\n" "8: template < class C > cacheEntry < C > :: cacheEntry ( ) : m_key@1 ( ) { }\n", tokenize(code, false, "test.cpp")); } void varid_in_class21() { const char code[] = "template \n" "class A::B {\n" " B();\n" " int x;\n" "};\n" "\n" "template \n" "A::B::B() : x(9) {}"; const char expected[] = "1: template < typename t1 , typename t2 >\n" "2: class A :: B {\n" "3: B ( ) ;\n" "4: int x@1 ;\n" "5: } ;\n" "6:\n" "7: template < typename t1 , typename t2 >\n" "8: A :: B < t1 , t2 > :: B ( ) : x@1 ( 9 ) { }\n"; ASSERT_EQUALS(expected, tokenize(code, false, "test.cpp")); } void varid_namespace_1() { // #7272 const char code[] = "namespace Blah {\n" " struct foo { int x;};\n" " struct bar {\n" " int x;\n" " union { char y; };\n" " };\n" "}"; ASSERT_EQUALS("1: namespace Blah {\n" "2: struct foo { int x@1 ; } ;\n" "3: struct bar {\n" "4: int x@2 ;\n" "5: union { char y@3 ; } ;\n" "6: } ;\n" "7: }\n", tokenize(code, false, "test.cpp")); } void varid_namespace_2() { // #7000 const char code[] = "namespace Ui {\n" " class C { int X; };\n" // X@1 "}\n" "\n" "class C {\n" " void dostuff();\n" " int X;\n" // X@2 "};\n" "\n" "void C::dostuff() {\n" " X = 0;\n" // X@2 "}"; const std::string actual = tokenize(code, false, "test.cpp"); ASSERT(actual.find("X@2 = 0") != std::string::npos); } void varid_initList() { const char code1[] = "class A {\n" " A() : x(0) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: A ( ) : x@1 ( 0 ) { }\n" "3: int x@1 ;\n" "4: } ;\n", tokenize(code1)); const char code2[] = "class A {\n" " A(int x) : x(x) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: A ( int x@1 ) : x@2 ( x@1 ) { }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code2)); const char code3[] = "class A {\n" " A(int x);\n" " int x;\n" "};\n" "A::A(int x) : x(x) {}"; ASSERT_EQUALS("1: class A {\n" "2: A ( int x@1 ) ;\n" "3: int x@2 ;\n" "4: } ;\n" "5: A :: A ( int x@3 ) : x@2 ( x@3 ) { }\n", tokenize(code3)); const char code4[] = "struct A {\n" " int x;\n" " A(int x) : x(x) {}\n" "};\n"; ASSERT_EQUALS("1: struct A {\n" "2: int x@1 ;\n" "3: A ( int x@2 ) : x@1 ( x@2 ) { }\n" "4: } ;\n", tokenize(code4)); const char code5[] = "class A {\n" " A(int x) noexcept : x(x) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: A ( int x@1 ) noexcept ( true ) : x@2 ( x@1 ) { }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code5)); const char code6[] = "class A {\n" " A(int x) noexcept(true) : x(x) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: A ( int x@1 ) noexcept ( true ) : x@2 ( x@1 ) { }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code6)); const char code7[] = "class A {\n" " A(int x) noexcept(false) : x(x) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: A ( int x@1 ) noexcept ( false ) : x@2 ( x@1 ) { }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code7)); const char code8[] = "class Foo : public Bar {\n" " explicit Foo(int i) : Bar(mi = i) { }\n" " int mi;\n" "};"; ASSERT_EQUALS("1: class Foo : public Bar {\n" "2: explicit Foo ( int i@1 ) : Bar ( mi@2 = i@1 ) { }\n" "3: int mi@2 ;\n" "4: } ;\n", tokenize(code8)); // #6520 const char code9[] = "class A {\n" " A(int x) : y(a?0:1), x(x) {}\n" " int x, y;\n" "};"; ASSERT_EQUALS("1: class A {\n" "2: A ( int x@1 ) : y@3 ( a ? 0 : 1 ) , x@2 ( x@1 ) { }\n" "3: int x@2 ; int y@3 ;\n" "4: } ;\n", tokenize(code9)); // #7123 const char code10[] = "class A {\n" " double *work;\n" " A(const Matrix &m) throw (e);\n" "};\n" "A::A(const Matrix &m) throw (e) : work(0)\n" "{}"; ASSERT_EQUALS("1: class A {\n" "2: double * work@1 ;\n" "3: A ( const Matrix & m@2 ) throw ( e ) ;\n" "4: } ;\n" "5: A :: A ( const Matrix & m@3 ) throw ( e ) : work@1 ( 0 )\n" "6: { }\n", tokenize(code10)); } void varid_initListWithBaseTemplate() { const char code1[] = "class A : B {\n" " A() : B(), x(0) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A : B < C , D > {\n" "2: A ( ) : B < C , D > ( ) , x@1 ( 0 ) { }\n" "3: int x@1 ;\n" "4: } ;\n", tokenize(code1)); const char code2[] = "class A : B {\n" " A(int x) : x(x) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A : B < C , D > {\n" "2: A ( int x@1 ) : x@2 ( x@1 ) { }\n" "3: int x@2 ;\n" "4: } ;\n", tokenize(code2)); const char code3[] = "class A : B {\n" " A(int x);\n" " int x;\n" "};\n" "A::A(int x) : x(x) {}"; ASSERT_EQUALS("1: class A : B < C , D > {\n" "2: A ( int x@1 ) ;\n" "3: int x@2 ;\n" "4: } ;\n" "5: A :: A ( int x@3 ) : x@2 ( x@3 ) { }\n", tokenize(code3)); const char code4[] = "struct A : B {\n" " int x;\n" " A(int x) : x(x) {}\n" "};\n"; ASSERT_EQUALS("1: struct A : B < C , D > {\n" "2: int x@1 ;\n" "3: A ( int x@2 ) : x@1 ( x@2 ) { }\n" "4: } ;\n", tokenize(code4)); const char code5[] = "class BCLass : public Ticket {\n" " BCLass();\n" " PClass* member;\n" "};\n" "BCLass::BCLass() : Ticket() {\n" " member = 0;\n" "}"; ASSERT_EQUALS("1: class BCLass : public Ticket < void > {\n" "2: BCLass ( ) ;\n" "3: PClass * member@1 ;\n" "4: } ;\n" "5: BCLass :: BCLass ( ) : Ticket < void > ( ) {\n" "6: member@1 = 0 ;\n" "7: }\n", tokenize(code5)); } void varid_initListWithScope() { const char code1[] = "class A : public B::C {\n" " A() : B::C(), x(0) {}\n" " int x;\n" "};"; ASSERT_EQUALS("1: class A : public B :: C {\n" "2: A ( ) : B :: C ( ) , x@1 ( 0 ) { }\n" "3: int x@1 ;\n" "4: } ;\n", tokenize(code1)); } void varid_operator() { { const std::string actual = tokenize( "class Foo\n" "{\n" "public:\n" " void operator=(const Foo &);\n" "};\n"); const char expected[] = "1: class Foo\n" "2: {\n" "3: public:\n" "4: void operator= ( const Foo & ) ;\n" "5: } ;\n"; ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenize( "struct Foo {\n" " void * operator new [](int);\n" "};\n"); const char expected[] = "1: struct Foo {\n" "2: void * operatornew[] ( int ) ;\n" "3: } ;\n"; ASSERT_EQUALS(expected, actual); } } void varid_throw() { // ticket #1723 const std::string actual = tokenize( "UserDefinedException* pe = new UserDefinedException();\n" "throw pe;\n"); const char expected[] = "1: UserDefinedException * pe@1 ; pe@1 = new UserDefinedException ( ) ;\n" "2: throw pe@1 ;\n"; ASSERT_EQUALS(expected, actual); } void varid_unknown_macro() { // #2638 - unknown macro const char code[] = "void f() {\n" " int a[10];\n" " AAA\n" " a[0] = 0;\n" "}"; const char expected[] = "1: void f ( ) {\n" "2: int a@1 [ 10 ] ;\n" "3: AAA\n" "4: a@1 [ 0 ] = 0 ;\n" "5: }\n"; ASSERT_EQUALS(expected, tokenize(code, false, "test.c")); } void varid_using() { // #3648 const char code[] = "using std::size_t;"; const char expected[] = "1: using unsigned long ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid_catch() { const char code[] = "void f() {\n" " try { dostuff(); }\n" " catch (exception &e) { }\n" "}"; const char expected[] = "1: void f ( ) {\n" "2: try { dostuff ( ) ; }\n" "3: catch ( exception & e@1 ) { }\n" "4: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid_functionPrototypeTemplate() { ASSERT_EQUALS("1: function < void ( ) > fptr@1 ;\n", tokenize("function fptr;")); } void varid_templatePtr() { ASSERT_EQUALS("1: std :: map < int , FooTemplate < int > * > dummy_member@1 [ 1 ] ;\n", tokenize("std::map*> dummy_member[1];")); } void varid_templateNamespaceFuncPtr() { ASSERT_EQUALS("1: KeyListT < float , & NIFFile :: getFloat > mKeyList@1 [ 4 ] ;\n", tokenize("KeyListT mKeyList[4];")); } void varid_templateArray() { ASSERT_EQUALS("1: VertexArrayIterator < float [ 2 ] > attrPos@1 ; attrPos@1 = m_AttributePos . GetIterator < float [ 2 ] > ( ) ;\n", tokenize("VertexArrayIterator attrPos = m_AttributePos.GetIterator();")); } void varid_templateParameter() { // #7046 set varid for "X": std::array Y; const char code[] = "const int X = 0;\n" "std::array Y;\n"; ASSERT_EQUALS("1: const int X@1 = 0 ;\n" "2: std :: array < int , X@1 > Y@2 ;\n", tokenize(code)); } void varid_templateUsing() { // #5781 #7273 const char code[] = "template using X = Y;\n" "X x;"; TODO_ASSERT_EQUALS("\nY x@1;\n", "1: template < class T > using X = Y < T , 4 > ;\n" "2: X < int > x@1 ;\n", tokenize(code)); } void varid_cppcast() { ASSERT_EQUALS("1: const_cast < int * > ( code ) [ 0 ] = 0 ;\n", tokenize("const_cast(code)[0] = 0;")); ASSERT_EQUALS("1: dynamic_cast < int * > ( code ) [ 0 ] = 0 ;\n", tokenize("dynamic_cast(code)[0] = 0;")); ASSERT_EQUALS("1: reinterpret_cast < int * > ( code ) [ 0 ] = 0 ;\n", tokenize("reinterpret_cast(code)[0] = 0;")); ASSERT_EQUALS("1: static_cast < int * > ( code ) [ 0 ] = 0 ;\n", tokenize("static_cast(code)[0] = 0;")); } void varid_variadicFunc() { ASSERT_EQUALS("1: int foo ( . . . ) ;\n", tokenize("int foo(...);")); } void varid_typename() { ASSERT_EQUALS("1: template < int d , class A , class B > struct S { } ;\n", tokenize("template struct S {};")); ASSERT_EQUALS("1: template < int d , typename A , typename B > struct S { } ;\n", tokenize("template struct S {};")); ASSERT_EQUALS("1: typename A a@1 ;\n", tokenize("typename A a;")); } void varid_rvalueref() { ASSERT_EQUALS("1: int & & a@1 ;\n", tokenize("int&& a;")); ASSERT_EQUALS("1: void foo ( int & & a@1 ) { }\n", tokenize("void foo(int&& a) {}")); ASSERT_EQUALS("1: class C {\n" "2: C ( int & & a@1 ) ;\n" "3: } ;\n", tokenize("class C {\n" " C(int&& a);\n" "};")); ASSERT_EQUALS("1: void foo ( int & & ) ;\n", tokenize("void foo(int&&);")); } void varid_arrayFuncPar() { ASSERT_EQUALS("1: void check ( const char fname@1 [ ] = 0 ) { }\n", tokenize("void check( const char fname[] = 0) { }")); } void varid_sizeofPassed() { ASSERT_EQUALS("1: void which_test ( ) {\n" "2: const char * argv@1 [ 2 ] = { \"./test_runner\" , \"TestClass\" } ;\n" "3: options args@2 ( sizeof ( argv@1 ) / sizeof ( argv@1 [ 0 ] ) , argv@1 ) ;\n" "4: args@2 . which_test ( ) ;\n" "5: }\n", tokenize("void which_test() {\n" " const char* argv[] = { \"./test_runner\", \"TestClass\" };\n" " options args(sizeof argv / sizeof argv[0], argv);\n" " args.which_test();\n" "}")); } void varid_classInFunction() { ASSERT_EQUALS("1: void AddSuppression ( ) {\n" "2: class QErrorLogger {\n" "3: void reportErr ( ErrorLogger :: ErrorMessage & msg@1 ) {\n" "4: }\n" "5: } ;\n" "6: }\n", tokenize("void AddSuppression() {\n" " class QErrorLogger {\n" " void reportErr(ErrorLogger::ErrorMessage &msg) {\n" " }\n" " }; \n" "}")); } void varid_pointerToArray() { ASSERT_EQUALS("1: int ( * a1@1 ) [ 10 ] ;\n" "2: void f1 ( ) {\n" "3: int ( * a2@2 ) [ 10 ] ;\n" "4: int ( & a3@3 ) [ 10 ] ;\n" "5: }\n" "6: struct A {\n" "7: int ( & a4@4 ) [ 10 ] ;\n" "8: int f2 ( int i@5 ) { return a4@4 [ i@5 ] ; }\n" "9: int f3 ( int ( & a5@6 ) [ 10 ] , int i@7 ) { return a5@6 [ i@7 ] ; }\n" "10: } ;\n" "11: int f4 ( int ( & a6@8 ) [ 10 ] , int i@9 ) { return a6@8 [ i@9 ] ; }\n", tokenize("int (*a1)[10];\n" // pointer to array of 10 ints "void f1() {\n" " int(*a2)[10];\n" " int(&a3)[10];\n" "}\n" "struct A {\n" " int(&a4)[10];\n" " int f2(int i) { return a4[i]; }\n" " int f3(int(&a5)[10], int i) { return a5[i]; }\n" "};\n" "int f4(int(&a6)[10], int i) { return a6[i]; }")); } void varid_cpp11initialization() { ASSERT_EQUALS("1: int i@1 { 1 } ;\n" "2: std :: vector < int > vec@2 { 1 , 2 , 3 } ;\n" "3: namespace n { int z@3 ; } ;\n" "4: int & j@4 { i@1 } ;\n" "5: int k@5 { 1 } ; int l@6 { 2 } ;\n", tokenize("int i{1};\n" "std::vector vec{1, 2, 3};\n" "namespace n { int z; };\n" "int& j{i};\n" "int k{1}, l{2};")); // #6030 ASSERT_EQUALS("1: struct S3 : public S1 , public S2 { } ;\n", tokenize("struct S3 : public S1, public S2 { };")); // #6058 ASSERT_EQUALS("1: class Scope { } ;\n", tokenize("class CPPCHECKLIB Scope { };")); // #6073 #6253 ASSERT_EQUALS("1: class A : public B , public C :: D , public E < F > :: G < H > {\n" "2: int i@1 ;\n" "3: A ( int i@2 ) : B { i@2 } , C :: D { i@2 } , E < F > :: G < H > { i@2 } , i@1 { i@2 } {\n" "4: int j@3 { i@2 } ;\n" "5: }\n" "6: } ;\n", tokenize("class A: public B, public C::D, public E::G {\n" " int i;\n" " A(int i): B{i}, C::D{i}, E::G{i} ,i{i} {\n" " int j{i};\n" " }\n" "};")); } void varid_inheritedMembers() { ASSERT_EQUALS("1: class A {\n" "2: int a@1 ;\n" "3: } ;\n" "4: class B : public A {\n" "5: void f ( ) ;\n" "6: } ;\n" "7: void B :: f ( ) {\n" "8: a@1 = 0 ;\n" "9: }\n", tokenize("class A {\n" " int a;\n" "};\n" "class B : public A {\n" " void f();\n" "};\n" "void B::f() {\n" " a = 0;\n" "}")); ASSERT_EQUALS("1: class A {\n" "2: int a@1 ;\n" "3: } ;\n" "4: class B : A {\n" "5: void f ( ) ;\n" "6: } ;\n" "7: void B :: f ( ) {\n" "8: a@1 = 0 ;\n" "9: }\n", tokenize("class A {\n" " int a;\n" "};\n" "class B : A {\n" " void f();\n" "};\n" "void B::f() {\n" " a = 0;\n" "}")); ASSERT_EQUALS("1: class A {\n" "2: int a@1 ;\n" "3: } ;\n" "4: class B : protected B , public A {\n" "5: void f ( ) ;\n" "6: } ;\n" "7: void B :: f ( ) {\n" "8: a@1 = 0 ;\n" "9: }\n", tokenize("class A {\n" " int a;\n" "};\n" "class B : protected B, public A {\n" " void f();\n" "};\n" "void B::f() {\n" " a = 0;\n" "}")); ASSERT_EQUALS("1: class A {\n" "2: int a@1 ;\n" "3: } ;\n" "4: class B : public A {\n" "5: void f ( ) {\n" "6: a@1 = 0 ;\n" "7: }\n" "8: } ;\n", tokenize("class A {\n" " int a;\n" "};\n" "class B : public A {\n" " void f() {\n" " a = 0;\n" " }\n" "};")); } void varid_header() { ASSERT_EQUALS("1: class A ;\n" "2: struct B {\n" "3: void setData ( const A & a@1 ) ;\n" "4: } ;\n", tokenize("class A;\n" "struct B {\n" " void setData(const A & a);\n" "}; ", false, "test.h")); } void varid_rangeBasedFor() { ASSERT_EQUALS("1: void reset ( Foo array@1 ) {\n" "2: for ( auto & e@2 : array@1 ) {\n" "3: foo ( e@2 ) ; }\n" "4: } ;\n", tokenize("void reset(Foo array) {\n" " for (auto& e : array)\n" " foo(e);\n" "};")); ASSERT_EQUALS("1: void reset ( Foo array@1 ) {\n" "2: for ( auto e@2 : array@1 ) {\n" "3: foo ( e@2 ) ; }\n" "4: } ;\n", tokenize("void reset(Foo array) {\n" " for (auto e : array)\n" " foo(e);\n" "};")); // Labels are no variables ASSERT_EQUALS("1: void foo ( ) {\n" "2: switch ( event . key . keysym . sym ) {\n" "3: case SDLK_LEFT : ;\n" "4: break ;\n" "5: case SDLK_RIGHT : ;\n" "6: delta = 1 ;\n" "7: break ;\n" "8: }\n" "9: }\n", tokenize("void foo() {\n" " switch (event.key.keysym.sym) {\n" " case SDLK_LEFT:\n" " break;\n" " case SDLK_RIGHT:\n" " delta = 1;\n" " break;\n" " }\n" "}", false, "test.c")); } void varid_structinit() { // #6406 ASSERT_EQUALS("1: void foo ( ) {\n" "2: struct ABC abc@1 ; abc@1 = { . a@2 = 0 , . b@3 = 1 } ;\n" "3: }\n", tokenize("void foo() {\n" " struct ABC abc = {.a=0,.b=1};\n" "}")); ASSERT_EQUALS("1: void foo ( ) {\n" "2: struct ABC abc@1 ; abc@1 = { . a@2 = abc@1 . a@2 , . b@3 = abc@1 . b@3 } ;\n" "3: }\n", tokenize("void foo() {\n" " struct ABC abc = {.a=abc.a,.b=abc.b};\n" "}")); } void varid_arrayinit() { // #7579 - no variable declaration in rhs ASSERT_EQUALS("1: void foo ( int * a@1 ) { int b@2 [ 1 ] = { x * a@1 [ 0 ] } ; }\n", tokenize("void foo(int*a) { int b[] = { x*a[0] }; }")); } void varidclass1() { const std::string actual = tokenize( "class Fred\n" "{\n" "private:\n" " int i;\n" "\n" " void foo1();\n" " void foo2()\n" " {\n" " ++i;\n" " }\n" "}\n" "\n" "Fred::foo1()\n" "{\n" " i = 0;\n" "}\n"); const char expected[] = "1: class Fred\n" "2: {\n" "3: private:\n" "4: int i@1 ;\n" "5:\n" "6: void foo1 ( ) ;\n" "7: void foo2 ( )\n" "8: {\n" "9: ++ i@1 ;\n" "10: }\n" "11: }\n" "12:\n" "13: Fred :: foo1 ( )\n" "14: {\n" "15: i@1 = 0 ;\n" "16: }\n"; ASSERT_EQUALS(expected, actual); } void varidclass2() { const std::string actual = tokenize( "class Fred\n" "{ void f(); };\n" "\n" "void A::foo1()\n" "{\n" " int i = 0;\n" "}\n" "\n" "void Fred::f()\n" "{\n" " i = 0;\n" "}\n"); const char expected[] = "1: class Fred\n" "2: { void f ( ) ; } ;\n" "3:\n" "4: void A :: foo1 ( )\n" "5: {\n" "6: int i@1 ; i@1 = 0 ;\n" "7: }\n" "8:\n" "9: void Fred :: f ( )\n" "10: {\n" "11: i = 0 ;\n" "12: }\n"; ASSERT_EQUALS(expected, actual); } void varidclass3() { const std::string actual = tokenize( "class Fred\n" "{ int i; void f(); };\n" "\n" "void Fred::f()\n" "{\n" " i = 0;\n" "}\n" "\n" "void A::f()\n" "{\n" " i = 0;\n" "}\n"); const char expected[] = "1: class Fred\n" "2: { int i@1 ; void f ( ) ; } ;\n" "3:\n" "4: void Fred :: f ( )\n" "5: {\n" "6: i@1 = 0 ;\n" "7: }\n" "8:\n" "9: void A :: f ( )\n" "10: {\n" "11: i = 0 ;\n" "12: }\n"; ASSERT_EQUALS(expected, actual); } void varidclass4() { const std::string actual = tokenize( "class Fred\n" "{ int i; void f(); };\n" "\n" "void Fred::f()\n" "{\n" " if (i) { }\n" " i = 0;\n" "}\n"); const char expected[] = "1: class Fred\n" "2: { int i@1 ; void f ( ) ; } ;\n" "3:\n" "4: void Fred :: f ( )\n" "5: {\n" "6: if ( i@1 ) { }\n" "7: i@1 = 0 ;\n" "8: }\n"; ASSERT_EQUALS(expected, actual); } void varidclass5() { const std::string actual = tokenize( "class A { };\n" "class B\n" "{\n" " A *a;\n" " B() : a(new A)\n" " { }\n" "};\n"); const char expected[] = "1: class A { } ;\n" "2: class B\n" "3: {\n" "4: A * a@1 ;\n" "5: B ( ) : a@1 ( new A )\n" "6: { }\n" "7: } ;\n"; ASSERT_EQUALS(expected, actual); } void varidclass6() { const std::string actual = tokenize( "class A\n" "{\n" " public:\n" " static char buf[20];\n" "};\n" "char A::buf[20];\n" "int main()\n" "{\n" " char buf[2];\n" " A::buf[10] = 0;\n" "}"); const char expected[] = "1: class A\n" "2: {\n" "3: public:\n" "4: static char buf@1 [ 20 ] ;\n" "5: } ;\n" "6: char A :: buf@1 [ 20 ] ;\n" "7: int main ( )\n" "8: {\n" "9: char buf@2 [ 2 ] ;\n" "10: A :: buf@1 [ 10 ] = 0 ;\n" "11: }\n"; ASSERT_EQUALS(expected, actual); } void varidclass7() { const std::string actual = tokenize( "int main()\n" "{\n" " char buf[2];\n" " A::buf[10] = 0;\n" "}"); const char expected[] = "1: int main ( )\n" "2: {\n" "3: char buf@1 [ 2 ] ;\n" "4: A :: buf [ 10 ] = 0 ;\n" "5: }\n"; ASSERT_EQUALS(expected, actual); } void varidclass8() { const char code[] ="class Fred {\n" "public:\n" " void foo(int d) {\n" " int i = bar(x * d);\n" " }\n" " int x;\n" "}\n"; const char expected[] = "1: class Fred {\n" "2: public:\n" "3: void foo ( int d@1 ) {\n" "4: int i@2 ; i@2 = bar ( x@3 * d@1 ) ;\n" "5: }\n" "6: int x@3 ;\n" "7: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass9() { const char code[] ="typedef char Str[10];" "class A {\n" "public:\n" " void f(Str &cl);\n" " void g(Str cl);\n" "}\n" "void Fred::f(Str &cl) {\n" " sizeof(cl);\n" "}"; const char expected[] = "1: class A {\n" "2: public:\n" "3: void f ( char ( & cl@1 ) [ 10 ] ) ;\n" "4: void g ( char cl@2 [ 10 ] ) ;\n" "5: }\n" "6: void Fred :: f ( char ( & cl@3 ) [ 10 ] ) {\n" "7: sizeof ( cl@3 ) ;\n" "8: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass10() { const char code[] ="class A {\n" " void f() {\n" " a = 3;\n" " }\n" " int a;\n" "};\n"; const char expected[] = "1: class A {\n" "2: void f ( ) {\n" "3: a@1 = 3 ;\n" "4: }\n" "5: int a@1 ;\n" "6: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass11() { const char code[] ="class Fred {\n" " int a;\n" " void f();\n" "};\n" "class Wilma {\n" " int a;\n" " void f();\n" "};\n" "void Fred::f() { a = 0; }\n" "void Wilma::f() { a = 0; }\n"; const char expected[] = "1: class Fred {\n" "2: int a@1 ;\n" "3: void f ( ) ;\n" "4: } ;\n" "5: class Wilma {\n" "6: int a@2 ;\n" "7: void f ( ) ;\n" "8: } ;\n" "9: void Fred :: f ( ) { a@1 = 0 ; }\n" "10: void Wilma :: f ( ) { a@2 = 0 ; }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass12() { const char code[] ="class Fred {\n" " int a;\n" " void f() { Fred::a = 0; }\n" "};\n"; const char expected[] = "1: class Fred {\n" "2: int a@1 ;\n" "3: void f ( ) { Fred :: a@1 = 0 ; }\n" "4: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass13() { const char code[] ="class Fred {\n" " int a;\n" " void f() { Foo::Fred::a = 0; }\n" "};\n"; const char expected[] = "1: class Fred {\n" "2: int a@1 ;\n" "3: void f ( ) { Foo :: Fred :: a = 0 ; }\n" "4: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass14() { // don't give friend classes varid { const char code[] ="class A {\n" "friend class B;\n" "}"; const char expected[] = "1: class A {\n" "2: friend class B ;\n" "3: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } { const char code[] ="class A {\n" "private: friend class B;\n" "}"; const char expected[] = "1: class A {\n" "2: private: friend class B ;\n" "3: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } } void varidclass15() { const char code[] = "class A {\n" " int a;\n" " int b;\n" " A();\n" "};\n" "A::A() : a(0) { b = 1; }"; const char expected[] = "1: class A {\n" "2: int a@1 ;\n" "3: int b@2 ;\n" "4: A ( ) ;\n" "5: } ;\n" "6: A :: A ( ) : a@1 ( 0 ) { b@2 = 1 ; }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass16() { const char code[] = "struct A;\n" "typedef bool (A::* FuncPtr)();\n" "struct A {\n" " FuncPtr pFun;\n" " void setPFun(int mode);\n" " bool funcNorm();\n" "};\n" "void A::setPFun(int mode) {\n" " pFun = &A::funcNorm;\n" "}"; const char expected[] = "1: struct A ;\n" "2:\n" "3: struct A {\n" "4: bool * pFun@1 ;\n" "5: void setPFun ( int mode@2 ) ;\n" "6: bool funcNorm ( ) ;\n" "7: } ;\n" "8: void A :: setPFun ( int mode@3 ) {\n" "9: pFun@1 = & A :: funcNorm ;\n" "10: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass17() { const char code[] = "class A: public B, public C::D {\n" " int i;\n" " A(int i): B(i), C::D(i), i(i) {\n" " int j(i);\n" " }\n" "};"; const char expected[] = "1: class A : public B , public C :: D {\n" "2: int i@1 ;\n" "3: A ( int i@2 ) : B ( i@2 ) , C :: D ( i@2 ) , i@1 ( i@2 ) {\n" "4: int j@3 ; j@3 = i@2 ;\n" "5: }\n" "6: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass18() { const char code[] = "class A {\n" " int a;\n" " int b;\n" " A();\n" "};\n" "A::A() : a{0} { b = 1; }"; const char expected[] = "1: class A {\n" "2: int a@1 ;\n" "3: int b@2 ;\n" "4: A ( ) ;\n" "5: } ;\n" "6: A :: A ( ) : a@1 { 0 } { b@2 = 1 ; }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidclass19() { const char code[] = "class A : public ::B {\n" " int a;\n" " A();\n" "};\n" "A::A() : ::B(), a(0) {}"; const char expected[] = "1: class A : public :: B {\n" "2: int a@1 ;\n" "3: A ( ) ;\n" "4: } ;\n" "5: A :: A ( ) : :: B ( ) , a@1 ( 0 ) { }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varid_classnameshaddowsvariablename() { const char code[] = "class Data;\n" "void strange_declarated(const Data& Data);\n" "void handleData(const Data& data) {\n" " strange_declarated(data);\n" "}\n"; const char expected[] = "1: class Data ;\n" "2: void strange_declarated ( const Data & Data@1 ) ;\n" "3: void handleData ( const Data & data@2 ) {\n" "4: strange_declarated ( data@2 ) ;\n" "5: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidnamespace1() { const char code[] = "namespace A {\n" " char buf[20];\n" "}\n" "int main() {\n" " return foo(A::buf);\n" "}"; const char expected[] = "1: namespace A {\n" "2: char buf@1 [ 20 ] ;\n" "3: }\n" "4: int main ( ) {\n" "5: return foo ( A :: buf@1 ) ;\n" "6: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void varidnamespace2() { const char code[] = "namespace A {\n" " namespace B {\n" " char buf[20];\n" " }\n" "}\n" "int main() {\n" " return foo(A::B::buf);\n" "}"; const char expected[] = "1: namespace A {\n" "2: namespace B {\n" "3: char buf@1 [ 20 ] ;\n" "4: }\n" "5: }\n" "6: int main ( ) {\n" "7: return foo ( A :: B :: buf@1 ) ;\n" "8: }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void usingNamespace1() { const char code[] = "namespace NS {\n" " class A { int x; void dostuff(); };\n" "}\n" "using namespace NS;\n" "void A::dostuff() { x = 0; }\n"; const char expected[] = "1: namespace NS {\n" "2: class A { int x@1 ; void dostuff ( ) ; } ;\n" "3: }\n" "4: using namespace NS ;\n" "5: void A :: dostuff ( ) { x@1 = 0 ; }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void usingNamespace2() { const char code[] = "class A { int x; void dostuff(); };\n" "using namespace NS;\n" "void A::dostuff() { x = 0; }\n"; const char expected[] = "1: class A { int x@1 ; void dostuff ( ) ; } ;\n" "2: using namespace NS ;\n" "3: void A :: dostuff ( ) { x@1 = 0 ; }\n"; ASSERT_EQUALS(expected, tokenize(code)); } void usingNamespace3() { const char code[] = "namespace A {\n" " namespace B {\n" " class C {\n" " double m;\n" " C();\n" " };\n" " }\n" "}\n" "using namespace A::B;\n" "C::C() : m(42) {}"; const char expected[] = "1: namespace A {\n" "2: namespace B {\n" "3: class C {\n" "4: double m@1 ;\n" "5: C ( ) ;\n" "6: } ;\n" "7: }\n" "8: }\n" "9: using namespace A :: B ;\n" "10: C :: C ( ) : m@1 ( 42 ) { }\n"; ASSERT_EQUALS(expected, tokenize(code)); } }; REGISTER_TEST(TestVarID) cppcheck-1.82/tools/000077500000000000000000000000001322667425100143745ustar00rootroot00000000000000cppcheck-1.82/tools/ci.py000066400000000000000000000045701322667425100153470ustar00rootroot00000000000000#!/usr/bin/env python # continuous integration # build daily reports (doxygen,coverage,etc) import datetime import time import subprocess import pexpect import glob import sys # Upload file to sourceforge web server using scp def upload(file_to_upload, destination): try: password = sys.argv[1] child = pexpect.spawn( 'scp ' + file_to_upload + ' danielmarjamaki,cppcheck@web.sourceforge.net:' + destination) # child.expect( # 'danielmarjamaki,cppcheck@web.sourceforge.net\'s password:') child.expect('Password:') child.sendline(password) child.interact() except (IOError, OSError, pexpect.TIMEOUT): pass # git push def gitpush(): try: password = sys.argv[1] child = pexpect.spawn('git push') child.expect("Enter passphrase for key '/home/daniel/.ssh/id_rsa':") child.sendline(password) child.interact() except (IOError, OSError, pexpect.TIMEOUT): pass def iconv(filename): p = subprocess.Popen(['file', '-i', filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) comm = p.communicate() if 'charset=iso-8859-1' in comm[0]: subprocess.call( ["iconv", filename, "--from=ISO-8859-1", "--to=UTF-8", "-o", filename]) # Generate daily webreport def generate_webreport(): for filename in glob.glob('*/*.cpp'): iconv(filename) subprocess.call( ["git", "commit", "-a", "-m", '"automatic conversion from iso-8859-1 formatting to utf-8"']) gitpush() subprocess.call(["rm", "-rf", "devinfo"]) subprocess.call(['nice', "./webreport.sh"]) upload('-r devinfo', 'htdocs/') subprocess.call(["make", "clean"]) subprocess.call(["rm", "-rf", "devinfo"]) # Perform a git pull. def gitpull(): try: password = sys.argv[1] child = pexpect.spawn('git pull') child.expect("Enter passphrase for key '/home/daniel/.ssh/id_rsa':") child.sendline(password) child.expect('Already up-to-date.') child.interact() except (IOError, OSError, pexpect.TIMEOUT): pass except pexpect.EOF: return True return False t0 = None while True: if datetime.date.today() != t0: print("generate daily reports") t0 = datetime.date.today() gitpull() generate_webreport() time.sleep(60) cppcheck-1.82/tools/daca2-addons.py000066400000000000000000000154661322667425100172020ustar00rootroot00000000000000#!/usr/bin/env python # # 1. Create a folder daca2-addons in your HOME folder # 2. Put cppcheck-O2 in daca2-addons. It should be built with all optimisations. # 3. Optional: Put a file called "suppressions.txt" in the daca2-addons folder. # 4. Optional: tweak FTPSERVER and FTPPATH in this script below. # 5. Run the daca2-addons script: python daca2-addons.py FOLDER import subprocess import sys import shutil import glob import os import datetime import time DEBIAN = ('ftp://ftp.se.debian.org/debian/', 'ftp://ftp.debian.org/debian/') def wget(filepath): filename = filepath if '/' in filepath: filename = filename[filename.rfind('/') + 1:] for d in DEBIAN: subprocess.call( ['nice', 'wget', '--tries=10', '--timeout=300', '-O', filename, d + filepath]) if os.path.isfile(filename): return True print('Sleep for 10 seconds..') time.sleep(10) return False def getpackages(folder): if not wget('ls-lR.gz'): return [] subprocess.call(['nice', 'gunzip', 'ls-lR.gz']) f = open('ls-lR', 'rt') lines = f.readlines() f.close() subprocess.call(['rm', 'ls-lR']) path = None archives = [] filename = None for line in lines: line = line.strip() if len(line) < 4: if filename: archives.append(path + '/' + filename) path = None filename = None elif line[:13 + len(folder)] == './pool/main/' + folder + '/': path = line[2:-1] elif path and '.orig.tar.' in line: filename = line[1 + line.rfind(' '):] for a in archives: print(a) return archives def handleRemoveReadonly(func, path, exc): import stat if not os.access(path, os.W_OK): # Is the error an access error ? os.chmod(path, stat.S_IWUSR) func(path) def removeAllExceptResults(): count = 5 while count > 0: count -= 1 filenames = [] filenames.extend(glob.glob('[A-Za-z0-9]*')) filenames.extend(glob.glob('.[a-z]*')) try: for filename in filenames: if os.path.isdir(filename): shutil.rmtree(filename, onerror=handleRemoveReadonly) elif filename != 'results.txt': os.remove(filename) except WindowsError as err: time.sleep(30) if count == 0: print('Failed to cleanup files/folders') print(err) sys.exit(1) continue except OSError as err: time.sleep(30) if count == 0: print('Failed to cleanup files/folders') print(err) sys.exit(1) continue count = 0 def removeLargeFiles(path): for g in glob.glob(path + '*'): if g == '.' or g == '..': continue if os.path.islink(g): continue if os.path.isdir(g): removeLargeFiles(g + '/') elif os.path.isfile(g) and g[-4:] != '.txt': statinfo = os.stat(g) if '/clang/INPUTS/' in path or statinfo.st_size > 100000: os.remove(g) def dumpfiles(path): ret = [] for g in glob.glob(path + '*'): if os.path.islink(g): continue if os.path.isdir(g): ret.extend(dumpfiles(path + g + '/')) elif os.path.isfile(g) and g[-5:] == '.dump': ret.append(g) return ret def scanarchive(filepath, jobs): # remove all files/folders except results.txt removeAllExceptResults() results = open('results.txt', 'at') results.write(DEBIAN[0] + filepath + '\n') results.close() if not wget(filepath): if not wget(filepath): results = open('results.txt', 'at') results.write('wget failed\n') results.close() return filename = filepath[filepath.rfind('/') + 1:] if filename[-3:] == '.gz': subprocess.call(['tar', 'xzvf', filename]) elif filename[-3:] == '.xz': subprocess.call(['tar', 'xJvf', filename]) elif filename[-4:] == '.bz2': subprocess.call(['tar', 'xjvf', filename]) # # List of skipped packages - which trigger known yet unresolved problems with cppcheck. # The issues on trac (http://trac.cppcheck.net) are given for reference # boost #3654 (?) # flite #5975 # insight#5184 # valgrind #6151 # gcc-arm - no ticket. Reproducible timeout in daca2 though as of 1.73/early 2016. # if filename[:5] == 'flite' or filename[:5] == 'boost' or filename[:7] == 'insight' or\ filename[:8] == 'valgrind' or filename[:7] == 'gcc-arm': results = open('results.txt', 'at') results.write('fixme: skipped package to avoid hang\n') results.close() return removeLargeFiles('') print('cppcheck ' + filename) p = subprocess.Popen( ['nice', '../cppcheck-O2', '--dump', '-D__GCC__', '--enable=style', '--error-exitcode=0', jobs, '.'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.communicate() results = open('results.txt', 'at') addons = sorted(glob.glob(os.path.expanduser('~/cppcheck/addons/*.py'))) for dumpfile in sorted(dumpfiles('')): for addon in addons: if 'cppcheckdata.py' in addon: continue p2 = subprocess.Popen(['nice', 'python', addon, dumpfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p2.communicate() results.write(comm[1]) results.close() FOLDER = None JOBS = '-j1' REV = None for arg in sys.argv[1:]: if arg[:6] == '--rev=': REV = arg[6:] elif arg[:2] == '-j': JOBS = arg else: FOLDER = arg if not FOLDER: print('no folder given') sys.exit(1) archives = getpackages(FOLDER) if len(archives) == 0: print('failed to load packages') sys.exit(1) print('Sleep for 10 seconds..') time.sleep(10) workdir = os.path.expanduser('~/daca2/') print('~/daca2/' + FOLDER) if not os.path.isdir(workdir + FOLDER): os.makedirs(workdir + FOLDER) os.chdir(workdir + FOLDER) try: results = open('results.txt', 'wt') results.write('STARTDATE ' + str(datetime.date.today()) + '\n') if REV: results.write('GIT-REVISION ' + REV + '\n') results.write('\n') results.close() for archive in archives: scanarchive(archive, JOBS) results = open('results.txt', 'at') results.write('DATE ' + str(datetime.date.today()) + '\n') results.close() except EOFError: pass # remove all files/folders except results.txt removeAllExceptResults() cppcheck-1.82/tools/daca2-download.py000066400000000000000000000102771322667425100175340ustar00rootroot00000000000000#!/usr/bin/env python # # Downloads all daca2 source code packages. # # Usage: # $ mkdir ~/daca2-packages && python daca2-download.py import subprocess import sys import shutil import glob import os import time DEBIAN = ('ftp://ftp.se.debian.org/debian/', 'ftp://ftp.debian.org/debian/') def wget(filepath): filename = filepath if '/' in filepath: filename = filename[filename.rfind('/') + 1:] for d in DEBIAN: subprocess.call( ['nice', 'wget', '--tries=10', '--timeout=300', '-O', filename, d + filepath]) if os.path.isfile(filename): return True print('Sleep for 10 seconds..') time.sleep(10) return False def getpackages(): if not wget('ls-lR.gz'): return [] subprocess.call(['nice', 'gunzip', 'ls-lR.gz']) f = open('ls-lR', 'rt') lines = f.readlines() f.close() subprocess.call(['rm', 'ls-lR']) path = None archives = [] filename = None for line in lines: line = line.strip() if len(line) < 4: if filename: archives.append(path + '/' + filename) path = None filename = None elif line[:12] == './pool/main/': path = line[2:-1] elif path and '.orig.tar.' in line: filename = line[1 + line.rfind(' '):] for a in archives: print(a) return archives def handleRemoveReadonly(func, path, exc): import stat if not os.access(path, os.W_OK): # Is the error an access error ? os.chmod(path, stat.S_IWUSR) func(path) def removeAll(): count = 5 while count > 0: count -= 1 filenames = [] filenames.extend(glob.glob('[#_A-Za-z0-9]*')) filenames.extend(glob.glob('.[A-Za-z]*')) try: for filename in filenames: if os.path.isdir(filename): shutil.rmtree(filename, onerror=handleRemoveReadonly) else: os.remove(filename) except WindowsError as err: time.sleep(30) if count == 0: print('Failed to cleanup files/folders') print(err) sys.exit(1) continue except OSError as err: time.sleep(30) if count == 0: print('Failed to cleanup files/folders') print(err) sys.exit(1) continue count = 0 def removeLargeFiles(path): for g in glob.glob(path + '*'): if g == '.' or g == '..': continue if os.path.islink(g): continue if os.path.isdir(g): removeLargeFiles(g + '/') elif os.path.isfile(g): # remove large files statinfo = os.stat(g) if statinfo.st_size > 100000: os.remove(g) # remove non-source files elif g[-2:] not in {'.C', '.c', '.H', '.h'} and g[-3:] != '.cc' and\ g[-4:] not in {'.cpp', '.cxx', '.c++', '.hpp', '.tpp', '.t++'}: os.remove(g) def downloadpackage(filepath, outpath): # remove all files/folders removeAll() if not wget(filepath): print('Failed to download ' + filepath) return filename = filepath[filepath.rfind('/') + 1:] if filename[-3:] == '.gz': subprocess.call(['tar', 'xzvf', filename]) elif filename[-3:] == '.xz': subprocess.call(['tar', 'xJvf', filename]) elif filename[-4:] == '.bz2': subprocess.call(['tar', 'xjvf', filename]) else: return removeLargeFiles('') for g in glob.glob('[#_A-Za-z0-9]*'): if os.path.isdir(g): subprocess.call(['tar', '-cJvf', outpath + filename[:filename.rfind('.')] + '.xz', g]) break workdir = os.path.expanduser('~/daca2-packages/tmp/') if not os.path.isdir(workdir): os.makedirs(workdir) os.chdir(workdir) packages = getpackages() if len(packages) == 0: print('failed to load packages') sys.exit(1) print('Sleep for 10 seconds..') time.sleep(10) for package in packages: downloadpackage(package, os.path.expanduser('~/daca2-packages/')) # remove all files/folders removeAll() cppcheck-1.82/tools/daca2-logs2git.sh000077500000000000000000000071111322667425100174350ustar00rootroot00000000000000#!/bin/bash #just in case... rm /tmp/daca_msg /tmp/daca_tmp_diff /tmp/daca_tmp_plus /tmp/daca_tmp_minus >& /dev/null base_url="http://cppcheck.sourceforge.net/devinfo/daca2-report/" echo ${base_url}daca2.html for site in `curl -s --compressed ${base_url}daca2.html | grep "^/g;' > ${site} done curl -s --compressed ${base_url}daca2.html > index.html git diff > /tmp/daca_tmp_diff grep "^+.*" /tmp/daca_tmp_diff > /tmp/daca_tmp_plus grep "^-.*" /tmp/daca_tmp_diff > /tmp/daca_tmp_minus plus_glob=`wc -l /tmp/daca_tmp_plus | cut -d' ' -f1` plus_error=`grep -c ":\ \(inconclusive\ \)\?error: " /tmp/daca_tmp_plus` plus_warning=`grep -c ":\ \(inconclusive\ \)\?warning: " /tmp/daca_tmp_plus` plus_style=`grep -c ":\ \(inconclusive\ \)\?style: " /tmp/daca_tmp_plus` plus_performance=`grep -c ":\ \(inconclusive\ \)\?performance: " /tmp/daca_tmp_plus` plus_portability=`grep -c ":\ \(inconclusive\ \)\?portability: " /tmp/daca_tmp_plus` #plus_information=`grep -c "]: (information)" /tmp/daca_tmp_plus` plus_crash=`grep -c "\ Crash?$" /tmp/daca_tmp_plus` plus_varid=`grep -c "called with varid 0\." /tmp/daca_tmp_plus` minus_glob=`wc -l /tmp/daca_tmp_minus | cut -d' ' -f1` minus_error=`grep -c ":\ \(inconclusive\ \)\?error: " /tmp/daca_tmp_minus` minus_warning=`grep -c ":\ \(inconclusive\ \)\?warning: " /tmp/daca_tmp_minus` minus_style=`grep -c ":\ \(inconclusive\ \)\?style: " /tmp/daca_tmp_minus` minus_performance=`grep -c ":\ \(inconclusive\ \)\?performance: " /tmp/daca_tmp_minus` minus_portability=`grep -c ":\ \(inconclusive\ \)\?portability: " /tmp/daca_tmp_minus` #minus_information=`grep -c "]: (information)" /tmp/daca_tmp_minus` minus_crash=`grep -c "\ Crash?$" /tmp/daca_tmp_minus` minus_varid=`grep -c "called with varid 0\." /tmp/daca_tmp_minus` files=`git ls-files` ID_stats=`awk '{ print $NF }' $files | grep "^\[.*\]$" | sort -n | uniq --count | sort -n` echo "Update `date`" >> /tmp/daca_msg echo "Updated: `git status --porcelain | grep daca | cut -d' ' -f3 | sed s/daca2-// | sed s/\.html// | tr '\n' ' '`" >> /tmp/daca_msg echo "all: new: $plus_glob gone: $minus_glob = $((plus_glob-minus_glob))" >> /tmp/daca_msg echo "error: new: $plus_error gone: $minus_error = $((plus_error-minus_error))" >> /tmp/daca_msg echo "warning: new: $plus_warning gone: $minus_warning = $((plus_warning-minus_warning))" >> /tmp/daca_msg echo "style: new: $plus_style gone: $minus_style = $((plus_style-minus_style))" >> /tmp/daca_msg echo "performance: new: $plus_performance gone: $minus_performance = $((plus_performance-minus_performance))" >> /tmp/daca_msg echo "portability: new: $plus_portability gone: $minus_portability = $((plus_portability-minus_portability))" >> /tmp/daca_msg #echo "information: new: $plus_information gone: $minus_information = $((plus_information-minus_information))" >> /tmp/daca_msg echo "crashes: new: $plus_crash gone: $minus_crash = $((plus_crash-minus_crash))" >> /tmp/daca_msg echo "varids: new: $plus_varid gone: $minus_varid = $((plus_varid-minus_varid))" >> /tmp/daca_msg echo "ID stats:" >> /tmp/daca_msg echo "${ID_stats}" >> /tmp/daca_msg cat /tmp/daca_msg git add -A git commit -F /tmp/daca_msg rm /tmp/daca_msg /tmp/daca_tmp_diff /tmp/daca_tmp_plus /tmp/daca_tmp_minus notify-send "daca logs done" cppcheck-1.82/tools/daca2-report.py000066400000000000000000000103671322667425100172400ustar00rootroot00000000000000#!/usr/bin/env python import os import sys def readdate(data): if data[:5] == 'DATE ': datepos = 0 else: datepos = data.find('\nDATE ') if datepos >= 0: datepos += 1 if datepos < 0: return None datestr = '' datepos += 5 while True: if datepos >= len(data): return None d = data[datepos] if '0' <= d <= '9': datestr += d elif d == '\n' or d == '\r': if len(datestr) == 8: return datestr[:4] + '-' + datestr[4:6] + '-' + datestr[6:] return None elif d != ' ' and d != '-': return None datepos += 1 daca2folder = os.path.expanduser('~/daca2/') path = '' for arg in sys.argv[1:]: if arg.startswith('--daca2='): daca2folder = arg[8:] if daca2folder[-1] != '/': daca2folder += '/' else: path = arg if path[-1] != '/': path += '/' mainpage = open(path + 'daca2.html', 'wt') mainpage.write('\n') mainpage.write('\n') mainpage.write('\n') mainpage.write('\n') mainpage.write('DACA2\n') mainpage.write('\n') mainpage.write('\n') mainpage.write('\n') mainpage.write('\n') mainpage.write('

DACA2

\n') mainpage.write('

Results when running latest (git head) Cppcheck on Debian.

\n') mainpage.write('

For performance reasons the analysis is limited. Files larger than 1mb are skipped. ' + 'If analysis of a file takes more than 10 minutes it may be stopped.

\n') mainpage.write('\n') mainpage.write( '' + '' + '' + '' + '' + '' + '' + '' + '' + '\n') lastupdate = None recent = [] daca2 = daca2folder for lib in (False, True): for a in "0123456789abcdefghijklmnopqrstuvwxyz": if lib: a = "lib" + a if not os.path.isfile(daca2 + a + '/results.txt'): continue f = open(daca2 + a + '/results.txt', 'rt') data = f.read() f.close() if 'ftp://' not in data: continue datestr = readdate(data) if datestr: if not lastupdate or datestr > lastupdate: lastupdate = datestr recent = [] if datestr == lastupdate: recent.append(a) else: datestr = '' mainpage.write( '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '\n') data = data.replace('&', '&') data = data.replace('<', '<') data = data.replace('>', '>') data = data.replace('\n', '\n') f = open(path + 'daca2-' + a + '.html', 'wt') f.write('\n') f.write('\n') f.write('\n') f.write('\n') f.write('DACA2 - ' + a + '\n') f.write('\n') f.write('\n') f.write('

DACA2 - ' + a + '

') f.write('
\n' + data + '
\n') f.write('\n') f.write('\n') f.close() mainpage.write('
NameDateErrorWarningPerformancePortabilityStyleCrashesVarID 0
' + a + '' + datestr + '' + str(data.count(': error:')) + '' + str(data.count(': warning:')) + '' + str(data.count(': performance:')) + '' + str(data.count(': portability:')) + '' + str(data.count(': style:')) + '' + str(data.count('Crash?')) + '' + str(data.count('with varid 0.')) + '
\n') if lastupdate: mainpage.write('

Last update: ' + lastupdate + '

') allrecent = '' for r in recent: allrecent = allrecent + '
' + r + '' mainpage.write('

Most recently updated:' + allrecent + '

') mainpage.write('\n') mainpage.write('\n') cppcheck-1.82/tools/daca2-search.cgi000077500000000000000000000044221322667425100173020ustar00rootroot00000000000000#!/usr/bin/python # cgi-script for searching the results import sys import glob import os import cgi import cgitb import re def getfiles(path, arguments): files = [] if 'folder' in arguments: files.append(path + '/daca2-' + arguments['folder'].value + '.html') else: files.extend(sorted(glob.glob(path+'/daca2-?.html'))) files.extend(sorted(glob.glob(path+'/daca2-lib?.html'))) return files def readlines(filename): if not os.path.isfile(filename): return [] f = open(filename, 'rt') lines = f.readlines() f.close() return lines def trimline(line): while len(line)>1 and (line[-1]=='\r' or line[-1]=='\n'): line = line[:-1] return line def matchline(line, id): return line.endswith('[' + id + ']') def doSearch(path,arguments): id = arguments['id'].value for g in getfiles(path, arguments): ftp = '' found = False for line in readlines(g): line = trimline(line) if line.startswith('ftp://'): ftp = line if matchline(line, id): found = True sys.stdout.write(ftp + '\n') elif line.find(': note:') < 0: found = False if found: sys.stdout.write(line + '\n') def summary(path, arguments): count = {} for g in getfiles(path, arguments): for line in readlines(g): line = trimline(line) res = re.match(r'.*: (error|warning|style|performance|portability):.*\[([a-zA-Z0-9]+)\]$', line) if res is None: continue id = res.group(2) if id in count: count[id] = count[id] + 1 else: count[id] = 1 print('') for id in sorted(count.keys()): print('') print('
' + id +''+str(count[id])+'
') sys.stdout.write('Content-type: text/html\r\n\r\n') sys.stdout.write('\n') cgitb.enable() arguments = cgi.FieldStorage() if 'id' in arguments: id = arguments['id'].value #id = 'oppositeInnerCondition' print(id) sys.stdout.write('
\n')
  doSearch('../htdocs/devinfo/daca2-report', arguments)
  #doSearch(os.path.expanduser('~/temp'), id)
  sys.stdout.write('
\n') else: summary('../htdocs/devinfo/daca2-report', arguments) #summary(os.path.expanduser('~/temp'), arguments) sys.stdout.write('\n') cppcheck-1.82/tools/daca2.py000066400000000000000000000153271322667425100157300ustar00rootroot00000000000000#!/usr/bin/env python # # 1. Create a folder daca2 in your HOME folder # 2. Put cppcheck-O2 in daca2. It should be built with all optimisations. # 3. Optional: Put a file called "suppressions.txt" in the daca2 folder. # 4. Optional: tweak FTPSERVER and FTPPATH in this script below. # 5. Run the daca2 script: python daca2.py FOLDER import argparse import subprocess import sys import shutil import glob import os import datetime import time import logging DEBIAN = ('ftp://ftp.se.debian.org/debian/', 'ftp://ftp.debian.org/debian/') def wget(filepath): filename = filepath if '/' in filepath: filename = filename[filename.rfind('/') + 1:] for d in DEBIAN: subprocess.call( ['nice', 'wget', '--tries=10', '--timeout=300', '-O', filename, d + filepath]) if os.path.isfile(filename): return True print('Sleep for 10 seconds..') time.sleep(10) return False def getpackages(folder): if not wget('ls-lR.gz'): return [] subprocess.call(['nice', 'gunzip', 'ls-lR.gz']) f = open('ls-lR', 'rt') lines = f.readlines() f.close() subprocess.call(['rm', 'ls-lR']) path = None archives = [] filename = None for line in lines: line = line.strip() if len(line) < 4: if filename: archives.append(path + '/' + filename) path = None filename = None elif line[:13 + len(folder)] == './pool/main/' + folder + '/': path = line[2:-1] elif path and '.orig.tar.' in line: filename = line[1 + line.rfind(' '):] for a in archives: print(a) return archives def handleRemoveReadonly(func, path, exc): import stat if not os.access(path, os.W_OK): # Is the error an access error ? os.chmod(path, stat.S_IWUSR) func(path) def removeAllExceptResults(): filenames = [] filenames.extend(glob.glob('[A-Za-z0-9]*')) filenames.extend(glob.glob('.[a-z]*')) for filename in filenames: count = 5 while count > 0: count -= 1 try: if os.path.isdir(filename): shutil.rmtree(filename, onerror=handleRemoveReadonly) elif filename != RESULTS_FILENAME: os.remove(filename) break except WindowsError as err: time.sleep(30) if count == 0: logging.error('Failed to cleanup {}: {}'.format(filename, err)) except OSError as err: time.sleep(30) if count == 0: logging.error('Failed to cleanup {}: {}'.format(filename, err)) def removeLargeFiles(path): for g in glob.glob(path + '*'): if g in {'.', '..'}: continue if os.path.islink(g): continue if os.path.isdir(g): # Remove test code if g.endswith('/testsuite') or g.endswith('/clang/INPUTS'): shutil.rmtree(g, onerror=handleRemoveReadonly) # Remove docs and examples ... that might be garbage elif g.endswith('/doc') or g.endswith('/examples'): shutil.rmtree(g, onerror=handleRemoveReadonly) else: removeLargeFiles(g + '/') elif os.path.isfile(g) and g[-4:] != '.txt': statinfo = os.stat(g) if statinfo.st_size > 1000000: try: os.remove(g) except OSError as err: logging.error('Failed to remove {}: {}'.format(g, err)) def strfCurrTime(fmt): return datetime.time.strftime(datetime.datetime.now().time(), fmt) def scanarchive(filepath, jobs, cpulimit): # remove all files/folders except RESULTS_FILENAME removeAllExceptResults() logging.info(DEBIAN[0] + filepath) if not wget(filepath): if not wget(filepath): logging.error('wget failed at {}', filepath) return filename = filepath[filepath.rfind('/') + 1:] if filename[-3:] == '.gz': subprocess.call(['tar', 'xzvf', filename]) elif filename[-3:] == '.xz': subprocess.call(['tar', 'xJvf', filename]) elif filename[-4:] == '.bz2': subprocess.call(['tar', 'xjvf', filename]) removeLargeFiles('') print(strfCurrTime('[%H:%M] cppcheck ') + filename) if cpulimit: cmd = 'cpulimit --limit=' + cpulimit else: cmd = 'nice --adjustment=1000' cmd = cmd + ' ../cppcheck-O2 -D__GCC__ --enable=style --inconclusive --error-exitcode=0 ' +\ '--exception-handling=stderr ' + jobs + ' --template=daca2 .' cmds = cmd.split() p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() if p.returncode == 0: logging.info(comm[1] + strfCurrTime('[%H:%M]')) elif 'cppcheck: error: could not find or open any of the paths given.' not in comm[0]: logging.error(comm[1] + strfCurrTime('[%H:%M]')) logging.error('Exit code is not zero! Crash?\n') parser = argparse.ArgumentParser(description='Checks debian source code') parser.add_argument('folder', metavar='FOLDER') parser.add_argument('--rev') parser.add_argument('--workdir', default='~/daca2') parser.add_argument('-j', '--jobs', default='-j1') parser.add_argument('--skip', default=[], action='append') parser.add_argument('--cpulimit') args = parser.parse_args() workdir = os.path.expanduser(args.workdir) if not os.path.isdir(workdir): print('workdir \'' + workdir + '\' is not a folder') sys.exit(1) workdir = os.path.join(workdir, args.folder) if not os.path.isdir(workdir): os.makedirs(workdir) RESULTS_FILENAME = 'results.txt' RESULTS_FILE = os.path.join(workdir, RESULTS_FILENAME) logging.basicConfig( filename=RESULTS_FILE, level=logging.INFO, format='%(message)s') print(workdir) archives = getpackages(args.folder) if len(archives) == 0: logging.critical('failed to load packages') sys.exit(1) if not os.path.isdir(workdir): os.makedirs(workdir) os.chdir(workdir) try: logging.info('STARTDATE ' + str(datetime.date.today())) logging.info('STARTTIME ' + strfCurrTime('%H:%M:%S')) if args.rev: logging.info('GIT-REVISION ' + args.rev + '\n') logging.info('') for archive in archives: if len(args.skip) > 0: a = archive[:archive.rfind('/')] a = a[a.rfind('/')+1:] if a in args.skip: continue scanarchive(archive, args.jobs, args.cpulimit) logging.info('DATE {}'.format(datetime.date.today())) logging.info('TIME {}'.format(strfCurrTime('%H:%M:%S'))) except EOFError: pass # remove all files/folders except RESULTS_FILENAME removeAllExceptResults() cppcheck-1.82/tools/dmake.cpp000066400000000000000000000422031322667425100161620ustar00rootroot00000000000000/* * 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 . */ // Generate Makefile for cppcheck #include #include #include #include #include #include #include "../cli/filelister.h" #include "../lib/pathmatch.h" static std::string builddir(std::string filename) { if (filename.compare(0,4,"lib/") == 0) filename = "$(SRCDIR)" + filename.substr(3); return filename; } static std::string objfile(std::string cppfile) { cppfile.erase(cppfile.rfind('.')); return builddir(cppfile + ".o"); } static void getDeps(const std::string &filename, std::vector &depfiles) { // Is the dependency already included? if (std::find(depfiles.begin(), depfiles.end(), filename) != depfiles.end()) return; std::ifstream f(filename.c_str()); if (! f.is_open()) { if (filename.compare(0, 4, "cli/") == 0 || filename.compare(0, 5, "test/") == 0) getDeps("lib" + filename.substr(filename.find('/')), depfiles); return; } if (filename.find(".c") == std::string::npos) depfiles.push_back(filename); std::string path(filename); if (path.find('/') != std::string::npos) path.erase(1 + path.rfind('/')); std::string line; while (std::getline(f, line)) { std::string::size_type pos1 = line.find("#include \""); if (pos1 == std::string::npos) continue; pos1 += 10; std::string::size_type pos2 = line.find('\"', pos1); std::string hfile(path + line.substr(pos1, pos2 - pos1)); if (hfile.find("/../") != std::string::npos) // TODO: Ugly fix hfile.erase(0, 4 + hfile.find("/../")); getDeps(hfile, depfiles); } } static void compilefiles(std::ostream &fout, const std::vector &files, const std::string &args) { for (unsigned int i = 0; i < files.size(); ++i) { bool external(files[i].compare(0,10,"externals/") == 0); fout << objfile(files[i]) << ": " << files[i]; std::vector depfiles; depfiles.push_back("lib/cxx11emu.h"); getDeps(files[i], depfiles); for (unsigned int dep = 0; dep < depfiles.size(); ++dep) fout << " " << depfiles[dep]; fout << "\n\t$(CXX) " << args << " $(CPPFLAGS) $(CFG) $(CXXFLAGS)" << (external?" -w":"") << " $(UNDEF_STRICT_ANSI) -c -o " << objfile(files[i]) << " " << builddir(files[i]) << "\n\n"; } } static void getCppFiles(std::vector &files, const std::string &path, bool recursive) { std::map filemap; const std::set extra; const std::vector masks; const PathMatch matcher(masks); FileLister::addFiles(filemap, path, extra, recursive, matcher); // add *.cpp files to the "files" vector.. for (std::map::const_iterator it = filemap.begin(); it != filemap.end(); ++it) { if (it->first.find(".cpp") != std::string::npos) files.push_back(it->first); } } static void makeConditionalVariable(std::ostream &os, const std::string &variable, const std::string &defaultValue) { os << "ifndef " << variable << '\n' << " " << variable << '=' << defaultValue << '\n' << "endif\n" << "\n"; } int main(int argc, char **argv) { const bool release(argc >= 2 && std::string(argv[1]) == "--release"); // Get files.. std::vector libfiles; getCppFiles(libfiles, "lib/", false); std::vector extfiles; extfiles.push_back("externals/simplecpp/simplecpp.cpp"); extfiles.push_back("externals/tinyxml/tinyxml2.cpp"); std::vector clifiles; getCppFiles(clifiles, "cli/", false); std::vector testfiles; getCppFiles(testfiles, "test/", false); std::vector toolsfiles; getCppFiles(toolsfiles, "tools/", false); if (libfiles.empty() && clifiles.empty() && testfiles.empty()) { std::cerr << "No files found. Are you in the correct directory?" << std::endl; return EXIT_FAILURE; } // QMAKE - lib/lib.pri { std::ofstream fout1("lib/lib.pri"); if (fout1.is_open()) { fout1 << "# no manual edits - this file is autogenerated by dmake\n\n"; fout1 << "include($$PWD/pcrerules.pri)\n"; fout1 << "include($$PWD/../externals/externals.pri)\n"; fout1 << "INCLUDEPATH += $$PWD\n"; fout1 << "HEADERS += $${PWD}/check.h \\\n"; for (unsigned int i = 0; i < libfiles.size(); ++i) { std::string fname(libfiles[i].substr(4)); if (fname.find(".cpp") == std::string::npos) continue; // shouldn't happen fname.erase(fname.find(".cpp")); fout1 << std::string(11, ' ') << "$${PWD}/" << fname << ".h"; if (i + 1 < testfiles.size()) fout1 << " \\\n"; } fout1 << "\n\nSOURCES += "; for (unsigned int i = 0; i < libfiles.size(); ++i) { fout1 << "$${PWD}/" << libfiles[i].substr(4); if (i < libfiles.size() - 1) fout1 << " \\\n" << std::string(11, ' '); } fout1 << "\n"; } } // QMAKE - test/testfiles.pri { std::ofstream fout1("test/testfiles.pri"); if (fout1.is_open()) { fout1 << "# no manual edits - this file is autogenerated by dmake\n\n"; fout1 << "INCLUDEPATH += ../externals/tinyxml\n"; fout1 << "\n\nSOURCES += "; for (unsigned int i = 0; i < testfiles.size(); ++i) { const std::string filename(testfiles[i].substr(5)); // Include only files containing tests in this listing. // I.e. filenames beginning with "test". if (filename.compare(0, 4, "test") == 0) { fout1 << "$${BASEPATH}/" << filename; if (i + 1 < testfiles.size()) fout1 << " \\\n" << std::string(11, ' '); } } fout1 << "\n"; } } static const char makefile[] = "Makefile"; std::ofstream fout(makefile, std::ios_base::trunc); if (!fout.is_open()) { std::cerr << "An error occurred while trying to open " << makefile << ".\n"; return EXIT_FAILURE; } fout << "# This file is generated by tools/dmake, do not edit.\n\n"; fout << "# To compile with rules, use 'make HAVE_RULES=yes'\n"; makeConditionalVariable(fout, "HAVE_RULES", "no"); // compiled patterns.. fout << "# folder where lib/*.cpp files are located\n"; makeConditionalVariable(fout, "SRCDIR", "lib"); fout << "ifeq ($(SRCDIR),build)\n" << " ifdef VERIFY\n" << " matchcompiler_S := $(shell python tools/matchcompiler.py --verify)\n" << " else\n" << " matchcompiler_S := $(shell python tools/matchcompiler.py)\n" << " endif\n" << "endif\n\n"; // explicit cfg dir.. fout << "ifdef CFGDIR\n" << " CFG=-DCFGDIR=\\\"$(CFGDIR)\\\"\n" << "else\n" << " CFG=\n" << "endif\n\n"; // enable backtrac fout << "RDYNAMIC=-rdynamic\n"; // The _GLIBCXX_DEBUG doesn't work in cygwin or other Win32 systems. fout << "# Set the CPPCHK_GLIBCXX_DEBUG flag. This flag is not used in release Makefiles.\n" << "# The _GLIBCXX_DEBUG define doesn't work in Cygwin or other Win32 systems.\n" << "ifndef COMSPEC\n" << " ifdef ComSpec\n" << " #### ComSpec is defined on some WIN32's.\n" << " COMSPEC=$(ComSpec)\n" << " endif # ComSpec\n" << "endif # COMSPEC\n" << "\n" << "ifdef COMSPEC\n" << " #### Maybe Windows\n" << " ifndef CPPCHK_GLIBCXX_DEBUG\n" << " CPPCHK_GLIBCXX_DEBUG=\n" << " endif # !CPPCHK_GLIBCXX_DEBUG\n" << "\n" << " ifeq ($(MSYSTEM),MINGW32 MINGW64)\n" << " LDFLAGS=-lshlwapi\n" << " else\n" << " RDYNAMIC=-lshlwapi\n" << " endif\n" << "else # !COMSPEC\n" << " uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n" << "\n" << " ifeq ($(uname_S),Linux)\n" << " ifndef CPPCHK_GLIBCXX_DEBUG\n" << " CPPCHK_GLIBCXX_DEBUG=-D_GLIBCXX_DEBUG\n" << " endif # !CPPCHK_GLIBCXX_DEBUG\n" << " endif # Linux\n" << "\n" << " ifeq ($(uname_S),GNU/kFreeBSD)\n" << " ifndef CPPCHK_GLIBCXX_DEBUG\n" << " CPPCHK_GLIBCXX_DEBUG=-D_GLIBCXX_DEBUG\n" << " endif # !CPPCHK_GLIBCXX_DEBUG\n" << " endif # GNU/kFreeBSD\n" << "\n" << "endif # COMSPEC\n" << "\n"; // tinymxl2 requires __STRICT_ANSI__ to be undefined to compile under CYGWIN. fout << "# Set the UNDEF_STRICT_ANSI flag to address compile time warnings\n" << "# with tinyxml2 and Cygwin.\n" << "ifdef COMSPEC\n" << " uname_S := $(shell uname -s)\n" << "\n" << " ifneq (,$(findstring CYGWIN,$(uname_S)))\n" << " UNDEF_STRICT_ANSI=-U__STRICT_ANSI__\n" << " endif # CYGWIN\n" << "endif # COMSPEC\n" << "\n"; // skip "-D_GLIBCXX_DEBUG" if clang, since it breaks the build makeConditionalVariable(fout, "CXX", "g++"); fout << "ifeq (clang++, $(findstring clang++,$(CXX)))\n" << " CPPCHK_GLIBCXX_DEBUG=\n" << "endif\n"; // Makefile settings.. if (release) { makeConditionalVariable(fout, "CXXFLAGS", "-std=c++0x -O2 -include lib/cxx11emu.h -DNDEBUG -Wall -Wno-sign-compare"); } else { // TODO: add more compiler warnings. // -Wlogical-op : doesn't work on older GCC // -Wsign-conversion : too many warnings // -Wunreachable-code : some GCC versions report lots of warnings makeConditionalVariable(fout, "CXXFLAGS", "-include lib/cxx11emu.h " "-pedantic " "-Wall " "-Wextra " "-Wabi " "-Wcast-qual " // "-Wconversion " // danmar: gives fp. for instance: unsigned int sizeof_pointer = sizeof(void *); "-Wfloat-equal " // "-Wlogical-op " "-Wmissing-declarations " "-Wmissing-format-attribute " "-Wno-long-long " // "-Woverloaded-virtual " // danmar: we get fp when overloading analyseWholeProgram() "-Wpacked " "-Wredundant-decls " "-Wshadow " // "-Wsign-conversion " // "-Wsign-promo " "-Wno-missing-field-initializers " "-Wno-missing-braces " // "-Wunreachable-code " "-Wno-sign-compare " // danmar: I don't like this warning, it's very rarelly a bug "-Wno-multichar " "$(CPPCHK_GLIBCXX_DEBUG) " "-g"); } fout << "ifeq (g++, $(findstring g++,$(CXX)))\n" << " override CXXFLAGS += -std=c++0x\n" << "else ifeq (clang++, $(findstring clang++,$(CXX)))\n" << " override CXXFLAGS += -std=c++0x\n" << "else ifeq ($(CXX), c++)\n" << " ifeq ($(shell uname -s), Darwin)\n" << " override CXXFLAGS += -std=c++0x\n" << " endif\n" << "endif\n" << "\n"; fout << "ifeq ($(HAVE_RULES),yes)\n" << " override CXXFLAGS += -DHAVE_RULES -DTIXML_USE_STL $(shell pcre-config --cflags)\n" << " ifdef LIBS\n" << " LIBS += $(shell pcre-config --libs)\n" << " else\n" << " LIBS=$(shell pcre-config --libs)\n" << " endif\n" << "endif\n\n"; makeConditionalVariable(fout, "PREFIX", "/usr"); makeConditionalVariable(fout, "INCLUDE_FOR_LIB", "-Ilib -Iexternals/simplecpp -Iexternals/tinyxml"); makeConditionalVariable(fout, "INCLUDE_FOR_CLI", "-Ilib -Iexternals/simplecpp -Iexternals/tinyxml"); makeConditionalVariable(fout, "INCLUDE_FOR_TEST", "-Ilib -Icli -Iexternals/simplecpp -Iexternals/tinyxml"); fout << "BIN=$(DESTDIR)$(PREFIX)/bin\n\n"; fout << "# For 'make man': sudo apt-get install xsltproc docbook-xsl docbook-xml on Linux\n"; fout << "DB2MAN?=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl\n"; fout << "XP=xsltproc -''-nonet -''-param man.charmap.use.subset \"0\"\n"; fout << "MAN_SOURCE=man/cppcheck.1.xml\n\n"; fout << "\n###### Object Files\n\n"; fout << "LIBOBJ = " << objfile(libfiles[0]); for (size_t i = 1; i < libfiles.size(); ++i) fout << " \\\n" << std::string(14, ' ') << objfile(libfiles[i]); fout << "\n\n"; fout << "EXTOBJ = " << objfile(extfiles[0]); for (size_t i = 1; i < extfiles.size(); ++i) fout << " \\\n" << std::string(14, ' ') << objfile(extfiles[i]); fout << "\n\n"; fout << "CLIOBJ = " << objfile(clifiles[0]); for (size_t i = 1; i < clifiles.size(); ++i) fout << " \\\n" << std::string(14, ' ') << objfile(clifiles[i]); fout << "\n\n"; fout << "TESTOBJ = " << objfile(testfiles[0]); for (size_t i = 1; i < testfiles.size(); ++i) fout << " \\\n" << std::string(14, ' ') << objfile(testfiles[i]); fout << "\n\n"; fout << ".PHONY: run-dmake tags\n\n"; fout << "\n###### Targets\n\n"; fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "all:\tcppcheck testrunner\n\n"; fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "test:\tall\n"; fout << "\t./testrunner\n\n"; fout << "check:\tall\n"; fout << "\t./testrunner -q\n\n"; fout << "checkcfg:\tcppcheck validateCFG\n"; fout << "\t./test/cfg/runtests.sh\n\n"; fout << "dmake:\ttools/dmake.o cli/filelister.o $(SRCDIR)/pathmatch.o $(SRCDIR)/path.o externals/simplecpp/simplecpp.o\n"; fout << "\t$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)\n\n"; fout << "run-dmake: dmake\n"; fout << "\t./dmake\n\n"; fout << "reduce:\ttools/reduce.o $(LIBOBJ) $(EXTOBJ)\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "clean:\n"; fout << "\trm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner reduce dmake cppcheck cppcheck.1\n\n"; fout << "man:\tman/cppcheck.1\n\n"; fout << "man/cppcheck.1:\t$(MAN_SOURCE)\n\n"; fout << "\t$(XP) $(DB2MAN) $(MAN_SOURCE)\n\n"; fout << "tags:\n"; fout << "\tctags -R --exclude=doxyoutput --exclude=test/cfg cli externals gui lib test\n\n"; fout << "install: cppcheck\n"; fout << "\tinstall -d ${BIN}\n"; fout << "\tinstall cppcheck ${BIN}\n"; fout << "\tinstall addons/*.py ${BIN}\n"; fout << "\tinstall addons/*/*.py ${BIN}\n"; fout << "\tinstall htmlreport/cppcheck-htmlreport ${BIN}\n"; fout << "ifdef CFGDIR \n"; fout << "\tinstall -d ${DESTDIR}${CFGDIR}\n"; fout << "\tinstall -m 644 cfg/* ${DESTDIR}${CFGDIR}\n"; fout << "endif\n\n"; fout << "# Validation of library files:\n"; fout << "ConfigFiles := $(wildcard cfg/*.cfg)\n"; fout << "ConfigFilesCHECKED := $(patsubst %.cfg,%.checked,$(ConfigFiles))\n"; fout << ".PHONY: validateCFG\n"; fout << "%.checked:%.cfg\n"; fout << "\txmllint --noout --relaxng cfg/cppcheck-cfg.rng $<\n"; fout << "validateCFG: ${ConfigFilesCHECKED}\n\n"; fout << "\n###### Build\n\n"; compilefiles(fout, libfiles, "${INCLUDE_FOR_LIB}"); compilefiles(fout, clifiles, "${INCLUDE_FOR_CLI}"); compilefiles(fout, testfiles, "${INCLUDE_FOR_TEST}"); compilefiles(fout, extfiles, ""); compilefiles(fout, toolsfiles, "${INCLUDE_FOR_LIB}"); return 0; } cppcheck-1.82/tools/dmake.vcproj000066400000000000000000000110221322667425100166760ustar00rootroot00000000000000 cppcheck-1.82/tools/extract_and_run_more_tests.sh000077500000000000000000000002431322667425100223560ustar00rootroot00000000000000#!/bin/bash cd ~/cppcheck rm -rf test1 python tools/extracttests.py --code=test1 test/testleakautovar.cpp cd ~/cppcheck/test1 ~/cppcheck/tools/run_more_tests.sh cppcheck-1.82/tools/extracttests.py000077500000000000000000000240501322667425100175070ustar00rootroot00000000000000#!/usr/bin/env python # # 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 . """ Extract test cases information from Cppcheck test file """ import os import sys import re class Extract: """ Read Cppcheck test file and create data representation """ # array that stores all the test cases nodes = [] def parseFile(self, filename): """ parse test file and add info to the nodes variable """ name = '[0-9a-zA-Z_]+' string = '\\"(.+)\\"' testclass = None functionName = None code = None fin = open(filename, 'r') for line in fin: # testclass starts res = re.match('class (' + name + ')', line) if res is not None: testclass = res.group(1) # end of testclass if re.match('};', line) is not None: testclass = None # function start res = re.match('\\s+void (' + name + ')\\(\\)', line) if res is not None: functionName = res.group(1) elif re.match('\\s+}', line) is not None: functionName = None if functionName is None: continue # check res = re.match('\s+check.*\(' + string, line) if res is not None: code = res.group(1) # code.. if code is not None: res = re.match('\\s+' + string, line) if res is not None: code = code + res.group(1) # assert res = re.match('\\s+ASSERT_EQUALS\\(\\"([^"]*)\\",', line) if res is not None and code is not None: node = {'testclass': testclass, 'functionName': functionName, 'code': code, 'expected': res.group(1)} self.nodes.append(node) code = None # close test file fin.close() def strtoxml(s): """Convert string to xml/html format""" return s.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') def trimname(name): """Trim test name. Trailing underscore and digits are removed""" while name[-1].isdigit(): name = name[:-1] if name[-1] == '_': name = name[:-1] return name def writeHtmlFile(nodes, functionName, filename, errorsOnly): """Write html file for a function name""" fout = open(filename, 'w') fout.write('\n') fout.write('\n') fout.write(' \n') fout.write('\n') fout.write('\n') fout.write('Home -- ') if errorsOnly: fout.write('All test cases') else: fout.write( 'Error test cases') fout.write('

') testclass = None num = 0 for node in nodes: if errorsOnly and node['expected'] == '': continue if trimname(node['functionName']) == functionName: num = num + 1 if not testclass: testclass = node['testclass'] fout.write( '

' + node['testclass'] + '::' + functionName + '

') fout.write('\n') fout.write( ' \n') fout.write(' ') fout.write('') fout.write( '') fout.write('\n') if testclass is not None: fout.write('
NrCodeExpected
' + str(num) + '
' + strtoxml(
                node['code']).replace('\\n', '\n') + '
' + strtoxml(node['expected']).replace('\\n', '
') + '
\n') fout.write('\n') fout.close() if len(sys.argv) <= 1 or '--help' in sys.argv: print('Extract test cases from test file') print( 'Syntax: extracttests.py [--html=folder] [--xml] [--code=folder] [--onlyTP] path/testfile.cpp') sys.exit(0) # parse command line xml = False filename = None htmldir = None codedir = None onlyTP = None for arg in sys.argv[1:]: if arg == '--xml': xml = True elif arg == '--onlyTP': onlyTP = True elif arg.startswith('--html='): htmldir = arg[7:] elif arg.startswith('--code='): codedir = arg[7:] elif arg.endswith('.cpp'): filename = arg else: print('Invalid option: ' + arg) sys.exit(1) # extract test cases if filename is not None: # parse test file e = Extract() e.parseFile(filename) # generate output if xml: print('') print('') count = 0 for node in e.nodes: s = ' ') elif htmldir is not None: if not htmldir.endswith('/'): htmldir += '/' if not os.path.exists(htmldir): os.mkdir(htmldir) findex = open(htmldir + 'index.htm', 'w') findex.write('\n') findex.write('\n') findex.write(' \n') findex.write('\n') findex.write('\n') findex.write('

' + filename + '

\n') functionNames = [] for node in e.nodes: functionname = trimname(node['functionName']) if functionname not in functionNames: functionNames.append(functionname) functionNames.sort() findex.write('\n') findex.write(' \n') for functionname in functionNames: findex.write(' ') numall = 0 numerr = 0 for node in e.nodes: if trimname(node['functionName']) == functionname: numall = numall + 1 if node['expected'] != '': numerr = numerr + 1 if numerr == 0: findex.write('') else: findex.write('') findex.write('') findex.write('\n') findex.write('
NameErrorsAll
' + functionname + '
0
' + str(numerr) + '
' + str(numall) + '
\n') findex.write('') findex.close() # create files for each functionName for functionName in functionNames: writeHtmlFile(e.nodes, functionName, htmldir + 'errors-' + functionName + '.htm', True) writeHtmlFile(e.nodes, functionName, htmldir + 'all-' + functionName + '.htm', False) elif codedir: testnum = 0 if not codedir.endswith('/'): codedir = codedir + '/' if not os.path.exists(codedir): os.mkdir(codedir) errors = open(codedir + 'errors.txt', 'w') for node in e.nodes: if onlyTP and node['expected'] == '': continue testnum = testnum + 1 functionName = node['functionName'] code = node['code'] code = code.replace('\\n', '\n') code = code.replace('\\"', '"') expected = node['expected'] filename = '0000' + str(testnum) + '-' filename = filename[-4:] filename += functionName + '.cpp' # source code fout = open(codedir + filename, 'w') fout.write(code) fout.close() # write 'expected' to errors.txt if expected != '': expected = expected.replace('\\n', '\n') expected = expected.replace('\\"', '"') expected = re.sub( '\\[test.cp?p?:', '[' + filename + ':', expected) errors.write(expected) errors.close() else: for node in e.nodes: print(node['functionName']) cppcheck-1.82/tools/generate_and_run_more_tests.sh000077500000000000000000000005501322667425100224770ustar00rootroot00000000000000#!/bin/bash # Run like: # cd ~/cppcheck # tools/generate_and_run_more_tests.sh set -e echo testleakautovar tools/run_more_tests.sh test/testleakautovar.cpp echo testmemleak tools/run_more_tests.sh test/testmemleak.cpp echo testnullpointer tools/run_more_tests.sh test/testnullpointer.cpp echo testuninitvar tools/run_more_tests.sh test/testuninitvar.cpp cppcheck-1.82/tools/git-pre-commit-cppcheck000066400000000000000000000023631322667425100207360ustar00rootroot00000000000000#!/bin/sh # Usage: add this file to your project's .git/hooks directory. Rename it to # just 'pre-commit'. # Now, when you change some files in repository and try to commit these # changes, git will run this script right before commit. Cppcheck will scan # changed/new files in repository. If it finds some issues, script returns with # exit code 1, rejecting commit. Otherwise, script returns 0, and you can # actually commit your changes. # # Example: # $ cat hello.c # int main() { # int *s = malloc(10); # } # $ git add hello.c # $ git commit # Checking hello.c... # [hello.c:3]: (error) Memory leak: s # [hello.c:2]: (error) The allocated size 10 is not a multiple of the underlying type's size. # # $ vim hello.c # $ cat hello.c # int main() { # } # $ git add hello.c # $ git commit # Checking hello.c... # $ if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # We should pass only added or modified C/C++ source files to cppcheck. changed_files=$(git diff-index --cached $against | \ grep -E '[MA] .*\.(c|cpp|cc|cxx)$' | cut -d' ' -f 2) if [ -n "$changed_files" ]; then cppcheck --error-exitcode=1 $changed_files exit $? fi cppcheck-1.82/tools/listErrorsWithoutCWE.py000077500000000000000000000013061322667425100210440ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import argparse import xml.etree.ElementTree as ET def main(): parser = argparse.ArgumentParser(description="List all error without a CWE assigned in CSV format") parser.add_argument("-F", metavar="filename", required=True, help="XML file containing output from: ./cppcheck --errorlist --xml-version=2") parsed = parser.parse_args() tree = ET.parse(vars(parsed)["F"]) root = tree.getroot() for child in root.iter("error"): if "cwe" not in child.attrib: print(child.attrib["id"], child.attrib["severity"], child.attrib["verbose"], sep=", ") if __name__ == "__main__": main() cppcheck-1.82/tools/matchcompiler.py000077500000000000000000000571521322667425100176120ustar00rootroot00000000000000#!/usr/bin/env python # # 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 . import io import os import sys import re import glob import argparse import errno class MatchCompiler: def __init__(self, verify_mode=False, show_skipped=False): self._verifyMode = verify_mode self._showSkipped = show_skipped self._reset() def _reset(self): self._rawMatchFunctions = [] self._matchFunctionCache = {} @staticmethod def _generateCacheSignature( pattern, endToken=None, varId=None, isFindMatch=False): sig = pattern if endToken: sig += '|ENDTOKEN' else: sig += '|NO-ENDTOKEN' if varId: sig += '|VARID' else: sig += '|NO-VARID' if isFindMatch: sig += '|ISFINDMATCH' else: sig += '|NORMALMATCH' return sig def _lookupMatchFunctionId( self, pattern, endToken=None, varId=None, isFindMatch=False): signature = self._generateCacheSignature( pattern, endToken, varId, isFindMatch) if signature in self._matchFunctionCache: return self._matchFunctionCache[signature] return None def _insertMatchFunctionId( self, id, pattern, endToken=None, varId=None, isFindMatch=False): signature = self._generateCacheSignature( pattern, endToken, varId, isFindMatch) # function signature should not be in the cache assert( self._lookupMatchFunctionId( pattern, endToken, varId, isFindMatch) is None) self._matchFunctionCache[signature] = id @staticmethod def _compileCmd(tok): if tok == '%any%': return 'true' elif tok == '%assign%': return 'tok->isAssignmentOp()' elif tok == '%bool%': return 'tok->isBoolean()' elif tok == '%char%': return '(tok->tokType()==Token::eChar)' elif tok == '%comp%': return 'tok->isComparisonOp()' elif tok == '%num%': return 'tok->isNumber()' elif tok == '%cop%': return 'tok->isConstOp()' elif tok == '%op%': return 'tok->isOp()' elif tok == '%or%': return '(tok->tokType() == Token::eBitOp && tok->str()==MatchCompiler::makeConstString("|") )' elif tok == '%oror%': return '(tok->tokType() == Token::eLogicalOp && tok->str()==MatchCompiler::makeConstString("||"))' elif tok == '%str%': return '(tok->tokType()==Token::eString)' elif tok == '%type%': return '(tok->isName() && tok->varId()==0U && !tok->isKeyword())' elif tok == '%name%': return 'tok->isName()' elif tok == '%var%': return '(tok->varId() != 0)' elif tok == '%varid%': return '(tok->isName() && tok->varId()==varid)' elif (len(tok) > 2) and (tok[0] == "%"): print("unhandled:" + tok) return ( '(tok->str()==MatchCompiler::makeConstString("' + tok + '"))' ) def _compilePattern(self, pattern, nr, varid, isFindMatch=False, tokenType="const Token"): if isFindMatch: ret = '\n ' + tokenType + ' * tok = start_tok;\n' returnStatement = 'continue;\n' else: arg2 = '' if varid: arg2 = ', const unsigned int varid' ret = '// pattern: ' + pattern + '\n' ret += 'static bool match' + \ str(nr) + '(' + tokenType + '* tok' + arg2 + ') {\n' returnStatement = 'return false;\n' tokens = pattern.split(' ') gotoNextToken = '' checked_varid = False for tok in tokens: if tok == '': continue ret += gotoNextToken gotoNextToken = ' tok = tok->next();\n' # if varid is provided, check that it's non-zero on first use if varid and '%varid%' in tok and not checked_varid: ret += ' if (varid==0U)\n' ret += ' throw InternalError(tok, "Internal error. Token::Match called with varid 0. ' +\ 'Please report this to Cppcheck developers");\n' checked_varid = True # [abc] if (len(tok) > 2) and (tok[0] == '[') and (tok[-1] == ']'): ret += ' if (!tok || tok->str().size()!=1U || !strchr("' + tok[1:-1] + '", tok->str()[0]))\n' ret += ' ' + returnStatement # a|b|c elif tok.find('|') > 0: tokens2 = tok.split('|') logicalOp = ' || ' if "" in tokens2: ret += ' if (tok && (' else: ret += ' if (!tok || !(' first = True for tok2 in tokens2: if tok2 == '': continue if not first: ret += logicalOp first = False ret += self._compileCmd(tok2) ret += '))\n' if "" in tokens2: ret += ' tok = tok->next();\n' gotoNextToken = '' else: ret += ' ' + returnStatement # !!a elif tok[0:2] == "!!": ret += ' if (tok && tok->str() == MatchCompiler::makeConstString("' + tok[2:] + '"))\n' ret += ' ' + returnStatement gotoNextToken = ' tok = tok ? tok->next() : NULL;\n' else: negatedTok = "!" + self._compileCmd(tok) # fold !true => false ; !false => true # this avoids cppcheck warnings about condition always being true/false if negatedTok == "!false": negatedTok = "true" elif negatedTok == "!true": negatedTok = "false" ret += ' if (!tok || ' + negatedTok + ')\n' ret += ' ' + returnStatement if isFindMatch: ret += ' return start_tok;\n' else: ret += ' return true;\n' ret += '}\n' return ret def _compileFindPattern(self, pattern, findmatchnr, endToken, varId): more_args = '' endCondition = '' if endToken: more_args += ', const Token * end' endCondition = ' && start_tok != end' if varId: more_args += ', unsigned int varid' ret = '// pattern: ' + pattern + '\n' ret += 'template static T * findmatch' + \ str(findmatchnr) + '(T * start_tok' + more_args + ') {\n' ret += ' for (; start_tok' + endCondition + \ '; start_tok = start_tok->next()) {\n' ret += self._compilePattern(pattern, -1, varId, True, 'T') ret += ' }\n' ret += ' return NULL;\n}\n' return ret @staticmethod def parseMatch(line, pos1): parlevel = 0 args = [] argstart = 0 pos = pos1 inString = False while pos < len(line): if inString: if line[pos] == '\\': pos += 1 elif line[pos] == '"': inString = False elif line[pos] == '"': inString = True elif line[pos] == '(': parlevel += 1 if parlevel == 1: argstart = pos + 1 elif line[pos] == ')': parlevel -= 1 if parlevel == 0: ret = [line[pos1:pos + 1]] ret.extend(args) ret.append(line[argstart:pos]) return ret elif line[pos] == ',' and parlevel == 1: args.append(line[argstart:pos]) argstart = pos + 1 pos += 1 return None @staticmethod def _isInString(line, pos1): pos = 0 inString = False while pos != pos1: if line[pos] == '\\': pos += 1 elif line[pos] == '"': inString = not inString pos += 1 return inString @staticmethod def _parseStringComparison(line, pos1): startPos = 0 pos = pos1 inString = False while pos < len(line): if inString: if line[pos] == '\\': pos += 1 elif line[pos] == '"': inString = False endPos = pos + 1 return startPos, endPos elif line[pos] == '"': startPos = pos inString = True pos += 1 return None @staticmethod def _compileVerifyTokenMatch( is_simplematch, verifyNumber, pattern, patternNumber, varId): more_args = '' if varId: more_args = ', const unsigned int varid' ret = 'static bool match_verify' + \ str(verifyNumber) + '(const Token *tok' + more_args + ') {\n' origMatchName = 'Match' if is_simplematch: origMatchName = 'simpleMatch' assert(varId is None) ret += ' bool res_compiled_match = match' + \ str(patternNumber) + '(tok' if varId: ret += ', varid' ret += ');\n' ret += ' bool res_parsed_match = Token::' + \ origMatchName + '(tok, "' + pattern + '"' if varId: ret += ', varid' ret += ');\n' ret += '\n' # Don't use assert() here, it's disabled for optimized builds. # We also need to verify builds in 'release' mode ret += ' if (res_parsed_match != res_compiled_match) {\n' # ret += ' std::cout << "res_parsed_match' + str(verifyNumber) +\ # ': " << res_parsed_match << ", res_compiled_match: " << res_compiled_match << "\\n";\n' # ret += ' if (tok)\n' # ret += ' std::cout << "tok: " << tok->str();\n' # ret += ' if (tok->next())\n' # ret += ' std::cout << "tok next: " << tok->next()->str();\n' ret += ' throw InternalError(tok, "Internal error.' +\ 'compiled match returned different result than parsed match: ' + pattern + '");\n' ret += ' }\n' ret += ' return res_compiled_match;\n' ret += '}\n' return ret def _replaceSpecificTokenMatch( self, is_simplematch, line, start_pos, end_pos, pattern, tok, varId): more_args = '' if varId: more_args = ',' + varId # Compile function or use previously compiled one patternNumber = self._lookupMatchFunctionId( pattern, None, varId, False) if patternNumber is None: patternNumber = len(self._rawMatchFunctions) + 1 self._insertMatchFunctionId( patternNumber, pattern, None, varId, False) self._rawMatchFunctions.append( self._compilePattern(pattern, patternNumber, varId)) functionName = "match" if self._verifyMode: verifyNumber = len(self._rawMatchFunctions) + 1 self._rawMatchFunctions.append( self._compileVerifyTokenMatch( is_simplematch, verifyNumber, pattern, patternNumber, varId)) # inject verify function functionName = "match_verify" patternNumber = verifyNumber return ( line[:start_pos] + functionName + str( patternNumber) + '(' + tok + more_args + ')' + line[start_pos + end_pos:] ) def _replaceTokenMatch(self, line, linenr, filename): while True: is_simplematch = False pos1 = line.find('Token::Match(') if pos1 == -1: is_simplematch = True pos1 = line.find('Token::simpleMatch(') if pos1 == -1: break res = self.parseMatch(line, pos1) if res is None: break # assert that Token::Match has either 2 or 3 arguments assert(len(res) == 3 or len(res) == 4) end_pos = len(res[0]) tok = res[1] raw_pattern = res[2] varId = None if len(res) == 4: varId = res[3] res = re.match(r'\s*"((?:.|\\")*?)"\s*$', raw_pattern) if res is None: if self._showSkipped: print(filename + ":" + str(linenr) + " skipping match pattern:" + raw_pattern) break # Non-const pattern - bailout pattern = res.group(1) line = self._replaceSpecificTokenMatch( is_simplematch, line, pos1, end_pos, pattern, tok, varId) return line @staticmethod def _compileVerifyTokenFindMatch( is_findsimplematch, verifyNumber, pattern, patternNumber, endToken, varId): more_args = '' if endToken: more_args += ', const Token * endToken' if varId: more_args += ', const unsigned int varid' ret = 'template < class T > static T * findmatch_verify' + \ str(verifyNumber) + '(T * tok' + more_args + ') {\n' origFindMatchName = 'findmatch' if is_findsimplematch: origFindMatchName = 'findsimplematch' assert(varId is None) ret += ' T * res_compiled_findmatch = findmatch' + \ str(patternNumber) + '(tok' if endToken: ret += ', endToken' if varId: ret += ', varid' ret += ');\n' ret += ' T * res_parsed_findmatch = Token::' + \ origFindMatchName + '(tok, "' + pattern + '"' if endToken: ret += ', endToken' if varId: ret += ', varid' ret += ');\n' ret += '\n' # Don't use assert() here, it's disabled for optimized builds. # We also need to verify builds in 'release' mode ret += ' if (res_parsed_findmatch != res_compiled_findmatch) {\n' ret += ' throw InternalError(tok, "Internal error. ' +\ 'compiled findmatch returned different result than parsed findmatch: ' + pattern + '");\n' ret += ' }\n' ret += ' return res_compiled_findmatch;\n' ret += '}\n' return ret def _replaceSpecificFindTokenMatch( self, is_findsimplematch, line, start_pos, end_pos, pattern, tok, endToken, varId): more_args = '' if endToken: more_args += ',' + endToken if varId: more_args += ',' + varId # Compile function or use previously compiled one findMatchNumber = self._lookupMatchFunctionId( pattern, endToken, varId, True) if findMatchNumber is None: findMatchNumber = len(self._rawMatchFunctions) + 1 self._insertMatchFunctionId( findMatchNumber, pattern, endToken, varId, True) self._rawMatchFunctions.append( self._compileFindPattern( pattern, findMatchNumber, endToken, varId)) functionName = "findmatch" if self._verifyMode: verifyNumber = len(self._rawMatchFunctions) + 1 self._rawMatchFunctions.append( self._compileVerifyTokenFindMatch( is_findsimplematch, verifyNumber, pattern, findMatchNumber, endToken, varId)) # inject verify function functionName = "findmatch_verify" findMatchNumber = verifyNumber return ( line[:start_pos] + functionName + str( findMatchNumber) + '(' + tok + more_args + ') ' + line[start_pos + end_pos:] ) def _replaceTokenFindMatch(self, line, linenr, filename): while True: is_findsimplematch = True pos1 = line.find('Token::findsimplematch(') if pos1 == -1: is_findsimplematch = False pos1 = line.find('Token::findmatch(') if pos1 == -1: break res = self.parseMatch(line, pos1) if res is None: break # assert that Token::find(simple)match has either 2, 3 or 4 arguments assert(len(res) >= 3 or len(res) < 6) g0 = res[0] tok = res[1] pattern = res[2] # Check for varId varId = None if not is_findsimplematch and "%varid%" in g0: if len(res) == 5: varId = res[4] else: varId = res[3] # endToken support. We resolve the overloaded type by checking if varId is used or not. # Function protoypes: # Token *findsimplematch(const Token *tok, const char pattern[]); # Token *findsimplematch(const Token *tok, const char pattern[], const Token *end); # Token *findmatch(const Token *tok, const char pattern[], unsigned int varId = 0); # Token *findmatch(const Token *tok, const char pattern[], const # Token *end, unsigned int varId = 0); endToken = None if ((is_findsimplematch and len(res) == 4) or (not is_findsimplematch and varId and (len(res) == 5)) or (not is_findsimplematch and varId is None and len(res) == 4)): endToken = res[3] res = re.match(r'\s*"((?:.|\\")*?)"\s*$', pattern) if res is None: if self._showSkipped: print(filename + ":" + str(linenr) + " skipping findmatch pattern:" + pattern) break # Non-const pattern - bailout pattern = res.group(1) line = self._replaceSpecificFindTokenMatch( is_findsimplematch, line, pos1, len(g0), pattern, tok, endToken, varId) return line def _replaceCStrings(self, line): while True: match = re.search('(==|!=) *"', line) if not match: break if self._isInString(line, match.start()): break res = self._parseStringComparison(line, match.start()) if res is None: break startPos = res[0] endPos = res[1] text = line[startPos + 1:endPos - 1] line = line[:startPos] + 'MatchCompiler::makeConstStringBegin' +\ text + 'MatchCompiler::makeConstStringEnd' + line[endPos:] line = line.replace('MatchCompiler::makeConstStringBegin', 'MatchCompiler::makeConstString("') line = line.replace('MatchCompiler::makeConstStringEnd', '")') return line def convertFile(self, srcname, destname, line_directive): self._reset() fin = io.open(srcname, "rt", encoding="utf-8") srclines = fin.readlines() fin.close() header = '#include "token.h"\n' header += '#include "errorlogger.h"\n' header += '#include "matchcompiler.h"\n' header += '#include \n' header += '#include \n' # header += '#include \n' code = '' linenr = 0 for line in srclines: linenr += 1 # Compile Token::Match and Token::simpleMatch line = self._replaceTokenMatch(line, linenr, srcname) # Compile Token::findsimplematch line = self._replaceTokenFindMatch(line, linenr, srcname) # Cache plain C-strings in C++ strings line = self._replaceCStrings(line) code += line # Compute matchFunctions strFunctions = '' for function in self._rawMatchFunctions: strFunctions += function lineno = '' if line_directive: lineno = '#line 1 "' + srcname + '"\n' fout = io.open(destname, 'wt', encoding="utf-8") fout.write(header + strFunctions + lineno + code) fout.close() def main(): # Main program # Argument handling parser = argparse.ArgumentParser( description='Compile Token::Match() calls into native C++ code') parser.add_argument('--verify', action='store_true', default=False, help='verify compiled matches against on-the-fly parser. Slow!') parser.add_argument('--show-skipped', action='store_true', default=False, help='show skipped (non-static) patterns') parser.add_argument('--read-dir', default="lib", help='directory from which files are read') parser.add_argument('--write-dir', default="build", help='directory into which files are written') parser.add_argument('--prefix', default="", help='prefix for build files') parser.add_argument('--line', action='store_true', default=False, help='add line directive to input files into build files') parser.add_argument('file', nargs='*', help='file to complile') args = parser.parse_args() lib_dir = args.read_dir build_dir = args.write_dir line_directive = args.line files = args.file # Check if we are invoked from the right place if not os.path.exists(lib_dir): print('Directory "' + lib_dir + '"not found.') sys.exit(-1) # Create build directory if needed try: os.makedirs(build_dir) except OSError as e: # due to race condition in case of parallel build, # makedirs may fail. Ignore that; if there's actual # problem with directory creation, it'll be caught # by the following isdir check if e.errno != errno.EEXIST: raise if not os.path.isdir(build_dir): raise Exception(build_dir + ' is not a directory') mc = MatchCompiler(verify_mode=args.verify, show_skipped=args.show_skipped) if not files: # select all *.cpp files in lib_dir for f in glob.glob(lib_dir + '/*.cpp'): files.append(f[len(lib_dir) + 1:]) # convert files for fi in files: pi = lib_dir + '/' + fi fo = args.prefix + fi po = build_dir + '/' + fo print(pi + ' => ' + po) mc.convertFile(pi, po, line_directive) if __name__ == '__main__': main() cppcheck-1.82/tools/parse-glibc.py000066400000000000000000000073531322667425100171460ustar00rootroot00000000000000#!/usr/bin/env python import glob import os def checknonnull(cfg, functionName, nonnull): pos1 = cfg.find('') if pos1 < 0: return pos2 = cfg.find('', pos1) if pos2 < 0: return functionCfg = cfg[pos1:pos2] s = None for argnr in range(10): argpos1 = functionCfg.find('') if argpos1 < 0: continue argpos2 = functionCfg.find('', argpos1) notnullpos = functionCfg.find('not-null', argpos1) if 0 <= notnullpos < argpos2: if s: s += ', ' + str(argnr) else: s = str(argnr) if s != nonnull: if not nonnull: nonnull = '' if not s: s = '' print(functionName + '\tglibc:' + nonnull + '\tcfg:' + s) def parseheader(cppcheckpath, filename): f = open(filename, 'rt') data = f.read() f.close() f = open(cppcheckpath + '/cfg/std.cfg', 'rt') stdcfg = f.read() f.close() f = open(cppcheckpath + '/cfg/posix.cfg', 'rt') posixcfg = f.read() f.close() while '/*' in data: pos1 = data.find('/*') pos2 = data.find('*/', pos1 + 2) data = data[:pos1] + data[pos2 + 2:] data = data.replace('\\\n', '') while '\n#' in data: pos1 = data.find('\n#') pos2 = data.find('\n', pos1 + 1) data = data[:pos1] + data[pos2:] while '\n__BEGIN' in data: pos1 = data.find('\n__BEGIN') pos2 = data.find('\n', pos1 + 1) data = data[:pos1] + data[pos2:] while '\n__END' in data: pos1 = data.find('\n__END') pos2 = data.find('\n', pos1 + 1) data = data[:pos1] + data[pos2:] data = data.replace('\n\n', '\n') data = data.replace('\t', ' ') data = data.replace(',\n ', ',') data = data.replace(')\n ', ',') data = data.replace(' ', ' ') output = [] for line in data.split('\n'): if (line[:7] != 'extern ' and ' extern ' not in line) or line[-1] != ';': continue functionNameEnd = line.find('(') - 1 if functionNameEnd < 0: continue while line[functionNameEnd] == ' ': functionNameEnd -= 1 if functionNameEnd < 10: continue functionNameStart = functionNameEnd while line[functionNameStart] == '_' or line[functionNameStart].isalnum(): functionNameStart -= 1 if functionNameStart < 10: continue if line[functionNameStart] != '*' and line[functionNameStart] != ' ': continue functionNameStart += 1 if not line[functionNameStart].isalpha(): continue functionName = line[functionNameStart:functionNameEnd + 1] nonnull = None nonnullStart = line.find('__nonnull') if nonnullStart >= 0: nonnullStart += 9 while nonnullStart < len(line) and line[nonnullStart] == ' ': nonnullStart += 1 if nonnullStart >= len(line) or line[nonnullStart] != '(': continue while line[nonnullStart] == '(': nonnullStart += 1 nonnullEnd = line.find(')', nonnullStart) nonnull = line[nonnullStart:nonnullEnd] checknonnull(stdcfg, functionName, nonnull) checknonnull(posixcfg, functionName, nonnull) if nonnull: s = functionName + ' ' + nonnull if s not in output: output.append(s) for f in glob.glob('/usr/include/*.h'): parseheader(os.path.expanduser('~/cppcheck'), f) cppcheck-1.82/tools/readme.md000066400000000000000000000043331322667425100161560ustar00rootroot00000000000000## Cppcheck developer and build tools ### * tools/matchcompiler.py The matchcompiler.py is a build script that performs a few code transformations to *.cpp* files under the *lib* directory. These transformations are related to the use of `Token::Match()` function and are intended to improve code performance. The transformed files are saved on the *build* directory. This tool is silently used when building the code with `SRCDIR=build`, that is: ```shell $ cd path/to/cppcheck $ make SRCDIR=build ``` Here is a simple example of the *matchcompiler.py* optimization. Suppose there is a file *example.cpp* under *lib/*: ```cpp // lib/example.cpp void f1() { Token::Match(tok, "abc"); } void f2() { const char *abc = "abc"; Token::Match(tok, abc); } ``` If you manually run *matchcompiler.py* from the main directory: ```shell $ cd path/to/cppcheck $ python tools/matchcompiler.py ``` A file *example.cpp* will be generated on the *build* directory: ```cpp // build/example.cpp #include "token.h" #include "errorlogger.h" #include #include static const std::string matchStr1("abc"); // pattern: abc static bool match1(const Token* tok) { if (!tok || !(tok->str()==matchStr1)/* abc */) return false; return true; } void f1() { match1(tok); } void f2() { const char *abc = "abc"; Token::Match(tok, abc); } ``` From this we can see that the usage of `Token::Match()` in `f1()` has been optimized, whereas the one in `f2()` couldn't be optimized (the string wasn't inline on the `Token::Match()` call). **The developer doesn't need to use this tool during development but should be aware of these optimizations**. *Building with this optimization, cppcheck can get a boost of 2x of speed-up.* ### * tools/dmake.cpp Automatically generates the main `Makefile` for Cppcheck (**the main `Makefile` should not be modified manually**). To build and run the `dmake` tool execute: ```shell $ cd path/to/cppcheck $ make dmake $ ./dmake ``` ### * tools/reduce.cpp Cppcheck tool that reduces code for a hang/false positive. To build the tool run: ```shell $ cd path/to/cppcheck $ make reduce ``` ### * tools/times.sh Script to generate a `times.log` file that contains timing information of the last 20 revisions. cppcheck-1.82/tools/reduce.cpp000066400000000000000000001036671322667425100163640ustar00rootroot00000000000000/* * 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 "cppcheck.h" #include "mathlib.h" #include "path.h" #include #include #include #include #include #include class ReduceSettings : public Settings { public: ReduceSettings() : filename(0), linenr(0), hang(false), maxtime(0) { } const char *filename; std::size_t linenr; bool hang; unsigned int maxtime; }; class CppcheckExecutor : public ErrorLogger { private: CppCheck cppcheck; std::string pattern; bool foundLine; std::time_t stopTime; public: explicit CppcheckExecutor(const ReduceSettings & settings) : ErrorLogger() , cppcheck(*this, false) , foundLine(false) , stopTime(0) { if (!settings.hang) pattern = ":" + MathLib::toString(settings.linenr) + "]"; cppcheck.settings() = settings; } bool run(const char filename[], unsigned int maxtime) { foundLine = false; stopTime = std::time(0) + maxtime; cppcheck.check(filename); return foundLine; } void reportOut(const std::string &/*outmsg*/) { } void reportErr(const ErrorLogger::ErrorMessage &msg) { if (!pattern.empty() && msg.toString(false).find(pattern) != std::string::npos) { foundLine = true; cppcheck.terminate(); } } void reportProgress(const std::string &/*filename*/, const char /*stage*/[], const std::size_t /*value*/) { if (std::time(0) > stopTime) { if (pattern.empty()) foundLine = true; else std::cerr << "timeout. You might want to use a longer --maxtime timeout" << std::endl; std::cout << "terminate" << std::endl; cppcheck.terminate(); } } }; static bool test(const ReduceSettings &settings, const std::vector &filedata, const std::size_t line1, const std::size_t line2) { std::string path(settings.filename); if (path.find_first_of("\\/") != std::string::npos) path = path.erase(1 + path.find_last_of("\\/")); else path.clear(); const std::string tempfilename(path + "__temp__" + std::strrchr(settings.filename,'.')); std::ofstream fout(tempfilename.c_str()); for (std::size_t i = 0; i < filedata.size(); i++) fout << ((i>=line1 && i<=line2) ? "" : filedata[i]) << std::endl; fout.close(); CppcheckExecutor cppcheck(settings); return cppcheck.run(tempfilename.c_str(), settings.maxtime); } static bool test(const ReduceSettings &settings, const std::vector &filedata, const std::size_t line) { return test(settings, filedata, line, line); } #ifdef GDB_HELPERS static void printstr(const std::vector &filedata, int i1, int i2) { std::cout << filedata.size(); for (int i = i1; i < i2; ++i) std::cout << i << ":" << filedata[i] << std::endl; } #endif static char getEndChar(const std::string &line) { std::size_t pos = line.find_last_not_of(" \t"); return (pos == std::string::npos) ? '\0' : line[pos]; } static std::vector readfile(const std::string &filename) { std::vector filedata; std::ifstream fin(filename.c_str()); std::string line; bool blockComment = false; while (std::getline(fin,line)) { // replace various space characters with space for (std::string::size_type pos = 0; pos < line.size(); ++pos) { if (line[pos] & 0x80 || std::isspace(line[pos])) line[pos] = ' '; } // remove block comments TODO: Handle /* inside strings if (blockComment) { if (line.find("*/") == std::string::npos) line.clear(); else { if (line.find("*/") + 2U == line.size()) line.clear(); else line = line.substr(line.find("*/") + 2U); blockComment = false; } } while (!blockComment && line.find("/*") != std::string::npos) { std::string::size_type pos = line.find("/*"); if (line.find("*/",pos) == std::string::npos) { blockComment = true; if (pos==0) line.clear(); else line = line.substr(0,pos); } else { blockComment = false; line = line.erase(pos, 2U + line.find("*/", pos) - pos); } } // Remove // comments if (line.find("//") != std::string::npos) line = line.substr(0, line.find("//")); // empty line if (line.find_first_not_of(" ") == std::string::npos) line.clear(); else { const std::string::size_type pos = line.find_first_not_of(" "); // remove spaces before leading # if (line[pos]=='#') line = line.substr(pos); } // remove trailing spaces while (!line.empty() && std::isspace(line[line.size()-1U])) line = line.substr(0, line.size() - 1U); filedata.push_back(line); } // put function declarations in a single line.. for (unsigned int linenr = 0U; linenr+1U < filedata.size(); ++linenr) { // Does this look like start of a function declaration? if (filedata[linenr].empty() || !std::isalpha(filedata[linenr][0U]) || getEndChar(filedata[linenr]) != ',' || filedata[linenr].find("(") == std::string::npos || filedata[linenr].find(")") != std::string::npos) continue; // Where does function declaration end? unsigned int linenr2 = linenr + 1U; while (linenr2 < filedata.size() && getEndChar(filedata[linenr2]) == ',' && filedata[linenr2].find("(") == std::string::npos && filedata[linenr2].find(")") == std::string::npos) ++linenr2; // If function declaration looks correct.. simplify it if (linenr2 < filedata.size() && getEndChar(filedata[linenr2]) == ';' && filedata[linenr2].find("(") == std::string::npos && filedata[linenr2].size() > 2U && filedata[linenr2].find(")") == filedata[linenr2].size() - 2U) { std::string code; for (unsigned int i = linenr; i <= linenr2; i++) { code = code + filedata[i]; filedata[i].clear(); } filedata[linenr] = code; } } // put #define statements in a single line.. for (unsigned int linenr = 0U; linenr+1U < filedata.size(); ++linenr) { // is this a multiline #define statement? if (filedata[linenr].compare(0,8,"#define ")!=0 || getEndChar(filedata[linenr])!='\\') continue; // where does statement end? unsigned int linenr2 = linenr + 1U; while (linenr2 < filedata.size() && getEndChar(filedata[linenr2]) == '\\') ++linenr2; // simplify if (linenr2 < filedata.size()) { std::string code; for (unsigned int i = linenr; i <= linenr2; i++) { code = code + filedata[i].substr(0,filedata[i].size() - 1U); filedata[i].clear(); } filedata[linenr] = code; } } return filedata; } static bool removeMacrosInGlobalScope(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; // Remove macros in global scope.. for (std::size_t i = 0; i < filedata.size(); i++) { const std::string line = (i==0U&&filedata.empty()) ? std::string(";") : filedata[i]; if (line.empty()) continue; const char startChar = line[0]; const char endChar = line[line.size() - 1U]; bool decl = bool(!std::isspace(startChar) && (endChar=='}' || endChar==';')); while (decl) { decl = false; // might be set to true below std::size_t pos = i + 1U; while (pos < filedata.size() && filedata[pos].empty()) ++pos; if (pos >= filedata.size()) break; const std::string &s = filedata[pos]; // possible macro : make sure it matches '[A-Z0-9_]+\(.*\)' std::string::size_type si = 0; while (si < s.size() && ((s[si]>='A' && s[si]<='Z') || (i>0 && s[si]>='0' && s[si]<='9') || s[si]=='_')) si++; while (si < s.size() && std::isspace(s[si])) si++; if (si == 0U || si >= s.size() || s[si] != '(') break; si++; unsigned int parlevel = 1; while (si < s.size() && parlevel >= 1U) { if (s[si] == '(') ++parlevel; else if (s[si] == ')') --parlevel; si++; } if (!(parlevel == 0U && si == s.size())) break; if (test(settings, filedata, pos)) { decl = true; filedata[pos].clear(); std::cout << "removed declaration at line " << pos << std::endl; changed = true; } else { std::cout << "kept declaration at line " << pos << std::endl; } } } return changed; } static bool removeBlocksOfCode(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; // Remove blocks of code.. for (std::size_t i = 0; i < filedata.size(); i++) { const std::string line = (i==0U&&filedata.empty()) ? std::string(";") : filedata[i]; if (line.empty()) continue; const char startChar = line[0]; const char endChar = line[line.size() - 1U]; // some kind of single line declaration if (std::isalpha(startChar) && endChar==';') { if (test(settings, filedata, i)) { filedata[i].clear(); std::cout << "removed declaration at line " << i << std::endl; changed = true; } else { std::cout << "kept declaration at line " << i << std::endl; } } // remove a function body below a '}' bool decl = bool(!std::isspace(startChar) && (endChar=='}' || endChar==';')); while (decl) { decl = false; // might be set to true below std::size_t pos = ++i; while (pos < filedata.size() && filedata[pos].empty()) ++pos; if ((pos+2U < filedata.size()) && (std::isalpha(filedata[pos].at(0)))) { // does code block start with "{" std::size_t pos2 = pos; // struct X { .. if (filedata[pos].find_first_of("();}") == std::string::npos && filedata[pos].at(filedata[pos].size()-1U) == '{') { } // function declaration .. else { if (filedata[pos].find("(") != std::string::npos && filedata[pos].find(")")==std::string::npos) { ++pos2; while (pos2+2U < filedata.size() && !filedata[pos2].empty() && filedata[pos2].find_first_of("(){}") == std::string::npos) ++pos2; if (filedata[pos2].find_first_of("({}")!=std::string::npos || filedata[pos2].find(")") == std::string::npos) break; } pos2++; if (pos2 < filedata.size() && !filedata[pos2].empty() && filedata[pos2].at(filedata[pos2].find_first_not_of(" ")) == ':') { pos2++; while (pos2 < filedata.size() && !filedata[pos2].empty() && filedata[pos2].at(filedata[pos2].find_first_not_of(" ")) == ',') pos2++; } if (pos2+2U >= filedata.size() || filedata[pos2] != "{") break; } pos2++; // find end of block.. int level = 0; while ((pos2 < filedata.size()) && (filedata[pos2].empty() || std::isspace(filedata[pos2].at(0)) || (std::isalpha(filedata[pos2].at(0)) && filedata[pos2].at(filedata[pos2].size()-1U) == ':') || filedata[pos2].compare(0,3,"#if")==0 || filedata[pos2].compare(0,3,"#el")==0 || filedata[pos2]=="#endif")) { if (filedata[pos2].compare(0,3,"#if") == 0) ++level; else if (filedata[pos2] == "#endif") --level; ++pos2; } if (level != 0) break; // does block of code end with a '}' if ((pos2 < filedata.size()) && (filedata[pos2] == "}" || filedata[pos2] == "};")) { if (test(settings, filedata, pos, pos2)) { for (i = pos; i <= pos2; i++) filedata[i].clear(); std::cout << "removed block of code at lines " << pos << "-" << pos2 << std::endl; decl = true; changed = true; } else { std::cout << "kept block of code at lines " << pos << "-" << pos2 << std::endl; } } } } } return changed; } static bool removeClassAndStructMembers(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; // remove class and struct members for (std::size_t i = 0; i + 2U < filedata.size(); i++) { if ((filedata[i].compare(0,6,"class ")==0 || filedata[i].compare(0,7,"struct ")==0) && filedata[i].find(";")==std::string::npos && filedata[i+1]=="{") { bool decl = true; for (std::size_t pos = i+2U; pos < filedata.size(); pos++) { const std::string line = filedata[pos]; if (line.empty()) continue; // count { and } unsigned int c1=0, c2=0; for (std::string::size_type c = 0; c < line.size(); c++) { if (line[c] == '{') ++c1; else if (line[c] == '}') ++c2; } if (c2>0 && (c1!=1 || c2!=1)) break; const char endChar = line[line.size() - 1U]; if (decl && (endChar == ';' || (c1==1 && c2==1 && endChar=='}'))) { if (test(settings, filedata, pos)) { std::cout << "removed struct/class declaration at line " << pos << std::endl; filedata[pos].clear(); changed = true; } else { std::cout << "kept struct/class declaration at line " << pos << std::endl; } } if (line[0] != '#') { if (decl && std::isalpha(line[0]) && endChar == ':') { decl = true; for (std::string::size_type linepos = 0U; linepos+1U < line.size(); ++linepos) decl &= (std::isspace(line[linepos]) || std::isalpha(line[linepos])); } else decl = bool(endChar == ';' || endChar == '}'); } } } } return changed; } static bool removeIfEndIf(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; // #if - #endif for (std::size_t i = 0; i < filedata.size(); ++i) { while (filedata[i].compare(0,3,"#if") == 0) { std::size_t pos2 = i + 1; while (pos2 < filedata.size() && filedata[pos2].empty()) ++pos2; if (pos2 < filedata.size() && filedata[pos2] == "#endif") { if (test(settings, filedata, i, pos2)) { std::cout << "Removed #if - #endif block at lines " << i << "-" << pos2 << std::endl; filedata[i].clear(); filedata[pos2].clear(); i = 0; changed = true; } else { std::cout << "Kept #if - #endif block at lines " << i << "-" << pos2 << std::endl; break; } } else { break; } } } // #ifndef UNUSED_ID for (std::size_t i = 0; i < filedata.size(); ++i) { if (filedata[i].compare(0,8,"#ifndef ") == 0) { bool erase = true; bool def = false; const std::string id(filedata[i].substr(8)); for (std::size_t i2 = 0; i2 < filedata.size(); i2++) { if (i2 == i) continue; if (filedata[i2].find(id) != std::string::npos) { if (!def && filedata[i2].compare(0,8,"#define ")==0) def = true; else erase = false; } } if (erase) { unsigned int level = 0; for (std::size_t i2 = i + 1U; i2 < filedata.size(); i2++) { if (filedata[i2].compare(0,3,"#if")==0) ++level; else if (filedata[i2] == "#else") break; else if (filedata[i2] == "#endif") { if (level > 0) --level; else { std::vector filedata2(filedata); filedata2[i].clear(); filedata2[i2].clear(); if (test(settings, filedata2, i)) { std::cout << "Removed #ifndef at line " << i << std::endl; filedata.swap(filedata2); changed = true; } else { std::cout << "Kept #ifndef at line " << i << std::endl; break; } break; } } } } } } return changed; } static bool removeUnusedDefines(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; for (std::size_t i = 0; i < filedata.size(); ++i) { if (filedata[i].compare(0,8,"#define ")==0 && filedata[i].find("\\")==std::string::npos) { // Try to remove macro.. if (test(settings, filedata, i)) { std::cout << "Removed #define at line " << i << std::endl; filedata[i].clear(); changed = true; } else { std::cout << "Kept #define at line " << i << std::endl; } } } return changed; } static bool removeSingleLines(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; bool decl = true; for (std::size_t i = 0; i < filedata.size(); ++i) { const std::string line = filedata[i]; if (line.empty()) continue; const char endChar = line[line.size() - 1U]; if (decl && endChar == ';') { if (test(settings, filedata, i)) { std::cout << "Removed statement at line " << i << std::endl; filedata[i].clear(); } else { std::cout << "Kept statement at line " << i << std::endl; } } else { decl = bool (endChar == ';' || endChar == '{' || endChar == '}'); } } return changed; } // Try to remove stuff from statements static bool cleanupStatements(const ReduceSettings &settings, std::vector &filedata) { bool changed = false; for (std::size_t i = 0; i < filedata.size(); ++i) { std::string line = filedata[i]; if (line.empty()) continue; for (std::string::size_type pos = 0U; pos < line.size(); ++pos) { // function parameter.. if (std::strchr("(,", line[pos])) { const std::string::size_type pos1 = (line[pos] == ',') ? pos : (pos + 1U); std::string::size_type pos2 = pos + 1; while (pos2 < line.size() && std::isspace(line[pos2])) ++pos2; while (pos2 < line.size() && (std::isalnum(line[pos2]) || line[pos2]=='_')) ++pos2; while (pos2 < line.size() && std::isspace(line[pos2])) ++pos2; if (pos2 >= pos+2U && pos2=line.size() || line[pos2]!='(') continue; pos2++; while (pos2 < line.size() && std::isalpha(line[pos2])) ++pos2; while (pos2 < line.size() && std::isspace(line[pos2])) ++pos2; if (pos2>=line.size() || line[pos2]!='*') continue; while (pos2 < line.size() && line[pos2]=='*') ++pos2; if (pos2] [--inconclusive] [--debug-warnings] [--max-configs=] [--platform=] [--library=] [--std=] filename [linenr]" << std::endl; return EXIT_FAILURE; } std::cout << "make sure " << (settings.hang ? "hang" : "false positive") << " can be reproduced" << std::endl; // Execute Cppcheck on the file.. { CppcheckExecutor cppcheck(settings); if (!cppcheck.run(settings.filename, settings.maxtime)) { std::cerr << "Can't reproduce false positive at line " << settings.linenr << std::endl; return EXIT_FAILURE; } } // Read file.. std::vector filedata(readfile(settings.filename)); // Write resulting code.. if (!test(settings, filedata, ~0)) { std::cerr << "Cleanup failed." << std::endl; return EXIT_FAILURE; } // Remove includes.. std::set headers; for (std::size_t i = 0; i < filedata.size(); i++) { if (filedata[i].compare(0,8,"#include")==0) { if (test(settings, filedata, i)) { std::cout << "removed #include : " << filedata[i] << std::endl; filedata[i].clear(); } else { std::string header = filedata[i]; header = header.substr(1U + header.find_first_of("\"<")); header = header.erase(header.find_last_of("\">")); if (headers.find(header) != headers.end()) { std::cerr << "Failed to reduce headers" << std::endl; return EXIT_FAILURE; } headers.insert(header); std::string path(settings.filename); if (path.find_first_of("\\/") != std::string::npos) path = path.erase(1 + path.find_last_of("\\/")); else path.clear(); std::cout << "expand #include : " << (path+header) << std::endl; std::vector data(readfile(path+header)); if (!data.empty()) { filedata[i].clear(); filedata.insert(filedata.begin()+i, data.begin(), data.end()); settings.linenr += data.size(); } } } } bool changed = true; while (changed) { changed = false; changed |= removeMacrosInGlobalScope(settings,filedata); changed |= removeBlocksOfCode(settings,filedata); changed |= removeClassAndStructMembers(settings,filedata); changed |= removeIfEndIf(settings,filedata); changed |= removeUnusedDefines(settings,filedata); if (settings.hang) { changed |= removeSingleLines(settings,filedata); changed |= cleanupStatements(settings,filedata); } } // Write resulting code.. { const std::string outfilename(std::string("__out__") + std::strrchr(settings.filename,'.')); std::ofstream fout; if (!print) fout.open(outfilename.c_str()); std::ostream &os = print ? std::cout : fout; for (std::size_t i = 0; i < filedata.size(); i++) { if (!filedata[i].empty()) os << filedata[i] << std::endl; } } return EXIT_SUCCESS; } cppcheck-1.82/tools/reduce.py000066400000000000000000000153461322667425100162260ustar00rootroot00000000000000#!/usr/bin/env python import subprocess import sys CMD = None EXPECTED = None SEGFAULT = False FILE = None BACKUPFILE = None for arg in sys.argv[1:]: if arg.startswith('--cmd='): CMD = arg[arg.find('=') + 1:] elif arg.startswith('--expected='): EXPECTED = arg[arg.find('=') + 1:] elif arg.startswith('--file='): FILE = arg[arg.find('=') + 1:] BACKUPFILE = FILE + '.bak' elif arg == '--segfault': SEGFAULT = True if CMD is None: print('Abort: No --cmd') sys.exit(1) if not SEGFAULT and EXPECTED is None: print('Abort: No --expected') sys.exit(1) if FILE is None: print('Abort: No --file') sys.exit(1) print('CMD=' + CMD) if SEGFAULT: print('EXPECTED=SEGFAULT') else: print('EXPECTED=' + EXPECTED) print('FILE=' + FILE) def runtool(): p = subprocess.Popen(CMD.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() if SEGFAULT: if p.returncode != 0: return True elif p.returncode == 0: out = comm[0] + '\n' + comm[1] if 'error:' not in out and EXPECTED in out: return True return False def writefile(filename, filedata): f = open(filename, 'wt') for line in filedata: f.write(line) f.close() def replaceandrun(what, filedata, i, line): print(what + ' ' + str(i + 1) + '/' + str(len(filedata)) + '..') bak = filedata[i] filedata[i] = line writefile(FILE, filedata) if runtool(): print('pass') writefile(BACKUPFILE, filedata) return True print('fail') filedata[i] = bak return False def replaceandrun2(what, filedata, i, line1, line2): print(what + ' ' + str(i + 1) + '/' + str(len(filedata)) + '..') bak1 = filedata[i] bak2 = filedata[i + 1] filedata[i] = line1 filedata[i + 1] = line2 writefile(FILE, filedata) if runtool(): print('pass') writefile(BACKUPFILE, filedata) else: print('fail') filedata[i] = bak1 filedata[i + 1] = bak2 def clearandrun(what, filedata, i1, i2): print(what + ' ' + str(i1 + 1) + '/' + str(len(filedata)) + '..') filedata2 = list(filedata) i = i1 while i <= i2 and i < len(filedata2): filedata2[i] = '' i = i + 1 writefile(FILE, filedata2) if runtool(): print('pass') writefile(BACKUPFILE, filedata2) return filedata2 print('fail') return filedata def removecomments(filedata): for i in range(len(filedata)): line = filedata[i] if '//' in line: replaceandrun('remove comment', filedata, i, line[:line.find('//')].rstrip()) def checkpar(line): par = 0 for c in line: if c == '(' or c == '[': par = par + 1 elif c == ')' or c == ']': par = par - 1 if par < 0: return False return par == 0 def combinelines(filedata): if len(filedata) < 3: return lines = [] for i in range(len(filedata) - 1): fd1 = filedata[i].rstrip() if fd1.endswith(','): fd2 = filedata[i + 1].lstrip() if fd2 != '': lines.append(i) chunksize = len(lines) while chunksize > 10: i = 0 while i < len(lines): i1 = i i2 = i + chunksize i = i2 if i2 > len(lines): i2 = len(lines) filedata2 = list(filedata) for line in lines[i1:i2]: filedata2[line] = filedata2[line].rstrip() + filedata2[line + 1].lstrip() filedata2[line + 1] = '' if replaceandrun('combine lines', filedata2, lines[i1] + 1, ''): filedata = filedata2 lines[i1:i2] = [] i = i1 chunksize = chunksize / 2 for line in lines: fd1 = filedata[line].rstrip() fd2 = filedata[line + 1].lstrip() replaceandrun2('combine lines', filedata, line, fd1 + fd2, '') def removedirectives(filedata): for i in range(len(filedata)): if filedata[i].lstrip().startswith('#'): replaceandrun('remove preprocessor directive', filedata, i, '') def removeblocks(filedata): if len(filedata) < 3: return filedata for i in range(len(filedata)): strippedline = filedata[i].strip() if len(strippedline) == 0: continue if strippedline[-1] not in ';{}': continue i1 = i + 1 while i1 < len(filedata) and filedata[i1].startswith('#'): i1 = i1 + 1 i2 = i1 indent = 0 while i2 < len(filedata): for c in filedata[i2]: if c == '}': indent = indent - 1 if indent == 0: indent = -100 elif c == '{': indent = indent + 1 if indent < 0: break i2 = i2 + 1 if indent == -100: indent = 0 if i2 == i1 or i2 >= len(filedata): continue if filedata[i2].strip() != '}' and filedata[i2].strip() != '};': continue if indent < 0: i2 = i2 - 1 filedata = clearandrun('remove codeblock', filedata, i1, i2) return filedata def removeline(filedata): stmt = True for i in range(len(filedata)): line = filedata[i] strippedline = line.strip() if len(strippedline) == 0: continue if stmt and strippedline[-1] == ';' and checkpar(line) and '{' not in line and '}' not in line: replaceandrun('remove line', filedata, i, '') elif stmt and '{' in strippedline and strippedline.find('}') == len(strippedline) - 1: replaceandrun('remove line', filedata, i, '') if strippedline[-1] in ';{}': stmt = True else: stmt = False # reduce.. print('Make sure error can be reproduced...') if not runtool(): print("Cannot reproduce") sys.exit(1) f = open(FILE, 'rt') filedata = f.readlines() f.close() writefile(BACKUPFILE, filedata) while True: filedata1 = list(filedata) print('remove comments...') removecomments(filedata) print('remove preprocessor directives...') removedirectives(filedata) print('remove blocks...') filedata = removeblocks(filedata) print('combine lines..') combinelines(filedata) print('remove line...') removeline(filedata) # if filedata and filedata2 are identical then stop if len(filedata1) == len(filedata): i = 0 while i < len(filedata1): if filedata[i] != filedata1[i]: break i = i + 1 if i == len(filedata1): break writefile(FILE, filedata) cppcheck-1.82/tools/run-coverity.sh000066400000000000000000000010471322667425100174000ustar00rootroot00000000000000#!/bin/bash PATH=$PATH:/home/danielmarjamaki/cov-analysis-linux64-2017.07/bin cd /home/danielmarjamaki/cppcheck-cov make clean echo Analyze nice cov-build --dir cov-int make echo Compressing tar czvf cppcheck.tgz cov-int echo Upload curl --form token=e74RRnWR6BVsn5LKdclfcA \ --form email=daniel.marjamaki@gmail.com \ --form file=@cppcheck.tgz \ --form version=`git log -1 --format=oneline | sed -r 's/([a-f0-9]{7}).*/\1/'` \ --form description="Development" \ https://scan.coverity.com/builds?project=cppcheck echo Done rm -rf cov-int cppcheck-1.82/tools/run_more_tests.sh000077500000000000000000000055301322667425100200060ustar00rootroot00000000000000#!/bin/bash # Script Used by generate_and_run_tests.sh set -e python tools/extracttests.py --code=test1 $1 cd test1 ../cppcheck -q . 2> 1.txt # (!x) => (x==0) sed -ri 's/([(&][ ]*)\!([a-z]+)([ ]*[&)])/\1\2==0\3/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x==0) => (0==x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*==[ ]*0([ ]*[&)])/\10==\2\3/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (0==x) => (!x) sed -ri 's/([(&][ ]*)0[ ]*==[ ]*([a-z]+)([ ]*[&)])/\1!\2\3/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # if (x) => (x!=0) sed -ri 's/(if[ ]*\([ ]*[a-z]+)([ ]*[&)])/\1!=0\2/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # while (x) => (x!=0) sed -ri 's/(while[ ]*\([ ]*[a-z]+)([ ]*[&)])/\1!=0\2/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x!=0) => (0!=x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*!=[ ]*0([ ]*[&)])/\10!=\2\3/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (0!=x) => (x) sed -ri 's/([(&][ ]*)0[ ]*!=[ ]*([a-z]+[ ]*[&)])/\1\2/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x < 0) => (0 > x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*<[ ]*(\-?[0-9]+)([ ]*[&)])/\1\3>\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x <= 0) => (0 >= x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*<=[ ]*(\-?[0-9]+)([ ]*[&)])/\1\3>=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x > 0) => (0 < x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*<=[ ]*(\-?[0-9]+)([ ]*[&)])/\1\3>=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x >= 0) => (0 <= x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*<=[ ]*(\-?[0-9]+)([ ]*[&)])/\1\3>=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x == 123) => (123 == x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*==[ ]*(\-?[0-9]+)([ ]*[&)])/\1\3==\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (x != 123) => (123 != x) sed -ri 's/([(&][ ]*)([a-z]+)[ ]*\!=[ ]*(\-?[0-9]+)([ ]*[&)])/\1\3!=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (0 < x) => (x > 0) sed -ri 's/([(&][ ]*)(\-?[0-9]+)[ ]*<[ ]*([a-z]+)([ ]*[&)])/\1\3>\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (0 <= x) => (x >= 0) sed -ri 's/([(&][ ]*)(\-?[0-9]+)[ ]*<=[ ]*([a-z]+)([ ]*[&)])/\1\3>=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (0 > x) => (x < 0) sed -ri 's/([(&][ ]*)(\-?[0-9]+)[ ]*<=[ ]*([a-z]+)([ ]*[&)])/\1\3>=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (0 >= x) => (x <= 0) sed -ri 's/([(&][ ]*)(\-?[0-9]+)[ ]*<=[ ]*([a-z]+)([ ]*[&)])/\1\3>=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (123 == x) => (x == 123) sed -ri 's/([(&][ ]*)(\-?[0-9]+)[ ]*==[ ]*([a-z]+)([ ]*[&)])/\1\3==\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt # (123 != x) => (x <= 123) sed -ri 's/([(&][ ]*)(\-?[0-9]+)[ ]*\!=[ ]*([a-z]+)([ ]*[&)])/\1\3!=\2\4/' *.cpp ../cppcheck -q . 2> 2.txt && diff 1.txt 2.txt cd .. rm -rf test1 cppcheck-1.82/tools/rundaca2.py000066400000000000000000000050101322667425100164410ustar00rootroot00000000000000#!/usr/bin/env python import subprocess import pexpect import os import sys import time START = 0 PASSWORD = '' for arg in sys.argv[1:]: if len(arg) == 1: START = '0123456789abcdefghijklmnopqrstuvwxyz'.find(arg) if START < 0: START = 0 else: PASSWORD = arg def compilecppcheck(CPPFLAGS): subprocess.call(['nice', 'make', 'clean']) subprocess.call(['nice', 'make', 'SRCDIR=build', 'CFGDIR=' + os.path.expanduser('~/cppcheck/cfg'), 'CXXFLAGS=-g -O2', 'CPPFLAGS=' + CPPFLAGS]) subprocess.call(['cp', 'cppcheck', os.path.expanduser('~/daca2/cppcheck-O2')]) def runcppcheck(rev, folder): subprocess.call(['rm', '-rf', os.path.expanduser('~/daca2/' + folder)]) subprocess.call(['nice', '--adjustment=19', 'python', os.path.expanduser('~/cppcheck/tools/daca2.py'), '--cpulimit=90', folder, '--rev=' + rev]) def daca2report(reportfolder): subprocess.call(['rm', '-rf', reportfolder]) subprocess.call(['mkdir', reportfolder]) subprocess.call(['python', os.path.expanduser('~/cppcheck/tools/daca2-report.py'), reportfolder]) # Upload file to sourceforge server using scp def upload(localfolder, webfolder): if len(PASSWORD) < 3: return tries = 1 while tries <= 5: try: child = pexpect.spawn( 'scp -r ' + localfolder + ' danielmarjamaki,cppcheck@web.sf.net:htdocs/' + webfolder) # child.expect('upload@trac.cppcheck.net\'s password:') child.expect('Password:') child.sendline(PASSWORD) child.interact() return except (IOError, OSError, pexpect.TIMEOUT, pexpect.EOF): print('Sleep for 10 seconds..') time.sleep(10) tries = tries + 1 def daca2(foldernum): folders = '0123456789abcdefghijklmnopqrstuvwxyz' folder = folders[foldernum % len(folders)] print('Daca2 folder=' + folder) os.chdir(os.path.expanduser('~/cppcheck')) subprocess.call(['git', 'pull']) p = subprocess.Popen(['git', 'show', '--format=%h'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() rev = comm[0] rev = rev[:rev.find('\n')] compilecppcheck('-DMAXTIME=600 -DDACA2') runcppcheck(rev, folder) runcppcheck(rev, 'lib' + folder) daca2report(os.path.expanduser('~/daca2-report')) upload(os.path.expanduser('~/daca2-report'), 'devinfo/') foldernum = START while True: daca2(foldernum) foldernum = foldernum + 1 cppcheck-1.82/tools/test_matchcompiler.py000077500000000000000000000175411322667425100206470ustar00rootroot00000000000000#!/usr/bin/env python # # 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 . import unittest import matchcompiler class MatchCompilerTest(unittest.TestCase): def setUp(self): self.mc = matchcompiler.MatchCompiler(verify_mode=False) def test_parseMatch(self): self.assertEqual(self.mc.parseMatch(' Token::Match(tok, ";") ', 2), [ 'Token::Match(tok, ";")', 'tok', ' ";"']) self.assertEqual(self.mc.parseMatch(' Token::Match(tok,', 2), None) # multiline Token::Match is not supported yet self.assertEqual(self.mc.parseMatch(' Token::Match(Token::findsimplematch(tok,")"), ";")', 2), [ 'Token::Match(Token::findsimplematch(tok,")"), ";")', 'Token::findsimplematch(tok,")")', ' ";"']) # inner function call def test_replaceTokenMatch(self): input = 'if (Token::Match(tok, "foobar")) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match1(tok)) {') input = 'if (Token::Match(tok->next()->next(), "foobar %type% %num%")) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match2(tok->next()->next())) {') input = 'if (Token::Match(tok, "foo\"special\"bar %num%")) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (match3(tok)) {') # test that non-static patterns get passed on unmatched input = 'if (Token::Match(tok, "struct " + varname)) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (Token::Match(tok, "struct " + varname)) {') # test that non-static patterns get passed on unmatched input = 'if (Token::Match(tok, "extern \"C\" " + varname)) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (Token::Match(tok, "extern \"C\" " + varname)) {') def test_replaceTokenMatchWithVarId(self): input = 'if (Token::Match(tok, "foobar %varid%", 123)) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match1(tok, 123)) {') input = 'if (Token::Match(tok->next()->next(), "%varid% foobar", tok->varId())) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (match2(tok->next()->next(), tok->varId())) {') input = 'if (Token::Match(tok, "foo\"special\"bar %type% %varid%", my_varid_cache)) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (match3(tok, my_varid_cache)) {') # test caching: reuse existing matchX() input = 'if (Token::Match(tok, "foobar %varid%", 123)) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match1(tok, 123)) {') # two in one line input = 'if (Token::Match(tok, "foobar2 %varid%", 123) || Token::Match(tok, "%type% %varid%", 123)) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match4(tok, 123) || match5(tok, 123)) {') def test_replaceTokenSimpleMatch(self): input = 'if (Token::simpleMatch(tok, "foobar")) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match1(tok)) {') input = 'if (Token::simpleMatch(tok->next()->next(), "foobar")) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (match1(tok->next()->next())) {') input = 'if (Token::simpleMatch(tok, "foo\"special\"bar")) {' output = self.mc._replaceTokenMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (match2(tok)) {') def test_replaceTokenFindSimpleMatch(self): input = 'if (Token::findsimplematch(tok, "foobar")) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (findmatch1(tok) ) {') input = 'if (Token::findsimplematch(tok->next()->next(), "foobar", tok->link())) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (findmatch2(tok->next()->next(), tok->link()) ) {') input = 'if (Token::findsimplematch(tok, "foo\"special\"bar")) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (findmatch3(tok) ) {') def test_replaceTokenFindMatch(self): input = 'if (Token::findmatch(tok, "foobar")) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (findmatch1(tok) ) {') # findmatch with varid input = 'if (Token::findmatch(tok, "foobar %varid%", tok->varId())) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual(output, 'if (findmatch2(tok, tok->varId()) ) {') # findmatch with end token input = 'if (Token::findmatch(tok->next()->next(), "foobar %type%", tok->link())) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (findmatch3(tok->next()->next(), tok->link()) ) {') # findmatch with end token and varid input = 'if (Token::findmatch(tok->next()->next(), "foobar %type% %varid%", tok->link(), 123)) {' output = self.mc._replaceTokenFindMatch(input, 0, "foo.cpp") self.assertEqual( output, 'if (findmatch4(tok->next()->next(), tok->link(), 123) ) {') def test_parseStringComparison(self): input = 'str == "abc"' # offset '5' is chosen as an abritary start offset to look for res = self.mc._parseStringComparison(input, 5) self.assertEqual(2, len(res)) self.assertEqual('str == MatchCompiler::makeConstString("abc")', input[:res[0]] + "MatchCompiler::makeConstString(" + input[res[0]:res[1]] + ")" + input[res[1]:]) input = 'str == "a\\"b\\"c"' res = self.mc._parseStringComparison(input, 5) self.assertEqual(2, len(res)) self.assertEqual('str == MatchCompiler::makeConstString("a\\"b\\"c")', input[:res[0]] + "MatchCompiler::makeConstString(" + input[res[0]:res[1]] + ")" + input[res[1]:]) def test_replaceCStrings(self): # str() == input = 'if (tok2->str() == "abc") {' output = self.mc._replaceCStrings(input) self.assertEqual('if (tok2->str() == MatchCompiler::makeConstString("abc")) {', output) # str() != input = 'if (tok2->str() != "xyz") {' output = self.mc._replaceCStrings(input) self.assertEqual('if (tok2->str() != MatchCompiler::makeConstString("xyz")) {', output) # strAt() input = 'if (match16(parent->tokAt(-3)) && tok->strAt(1) == ")")' output = self.mc._replaceCStrings(input) self.assertEqual( 'if (match16(parent->tokAt(-3)) && tok->strAt(1) == MatchCompiler::makeConstString(")"))', output) if __name__ == '__main__': unittest.main() cppcheck-1.82/tools/test_showtimetop5.sh000077500000000000000000000002031322667425100204340ustar00rootroot00000000000000#!/bin/bash if [[ "$(./cppcheck --showtime=top5 cli/cmdlineparser.h --language=c++ --quiet | wc -l)" != 7 ]] ; then false fi cppcheck-1.82/tools/testrunnerify_code.sh000077500000000000000000000001301322667425100206400ustar00rootroot00000000000000#!/bin/bash sed s@\"@\\\\\"@g | sed s@^@\"@ $1 | sed s@\$@\\\\n\"@ | sed 's@\t@ @g' cppcheck-1.82/tools/times-tags.sh000077500000000000000000000011651322667425100170130ustar00rootroot00000000000000#!/bin/bash # # Simple script to generate times-tags.log that contains timing information for a range of revisions # Typically these commands shall be used to get times.txt: # mkdir src # cp lib/* src/ <- fill src/ with some source code # tools/times-tags.sh # gcc -o tools/times-tags tools/times-tags.c # tools/times-tags rm times-tags.txt for i in $(seq $1 $2); do echo "1.$i" echo "1.$i" >> times-tags.txt git checkout "1.$i" -b "$i" make clean make -j4 > /dev/null /usr/bin/time -a -o times-tags.txt ./cppcheck sources -q 2> /dev/null git checkout master git branch -D "$i" done cppcheck-1.82/tools/times-vs.py000066400000000000000000000033611322667425100165200ustar00rootroot00000000000000#!/usr/bin/env python3 # Times script using Visual Studio compiler in Windows # # This script assumes that you have: # Python 3 # Visual Studio (script assumes VS2013, manipulate the sed command otherwise) # Cygwin64 for the sed command # Command line svn. TortoiseSVN with that feature selected works. # # Usage: # Open VS command prompt. # cd c:\users\... # svn checkout https://github.com/danmar/cppcheck/trunk cppcheck-svn # cd cppcheck-svn # c:\python34\python.exe times-vs.py rev1:rev2 import subprocess import glob import re import sys if len(sys.argv) != 2: print('revisions not specified') sys.exit(1) res = re.match(r'([0-9]+):([0-9]+)', sys.argv[1]) if res is None: print('invalid format, 11111:22222') sys.exit(1) rev1 = int(res.group(1)) rev2 = int(res.group(2)) if rev1 > rev2 or rev1 < 10000 or rev2 > 20000 or rev2 - rev1 > 500: print('range, aborting') sys.exit(1) print('Revisions: ' + str(rev1) + ':' + str(rev2)) f = open('results.txt', 'wt') f.write('\n') f.close() for rev in range(rev1, rev2): subprocess.call(['svn', 'revert', '-R', '.']) subprocess.call(['svn', 'up', '-r' + str(rev)]) for vcxproj in glob.glob('*/*.vcxproj'): subprocess.call([r'c:\cygwin64\bin\sed.exe', '-i', 's/140/120/', vcxproj]) subprocess.call('msbuild cppcheck.sln /t:build /p:configuration=Release,platform=x64'.split()) print('Revision:' + str(rev)) p = subprocess.Popen(r'bin\cppcheck.exe src -q --showtime=summary'.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() f = open('results.txt', 'at') f.write('\nREV ' + str(rev) + '\n') f.write(comm[0].decode('utf-8')) f.close() cppcheck-1.82/tools/times.c000066400000000000000000000022171322667425100156630ustar00rootroot00000000000000#include #include #include char *replace(char *str, char before, char after) { while (strchr(str,before)) *strchr(str,before) = after; return str; } int main() { FILE *f = fopen("times.log", "rt"); if (!f) return 1; char lines[0xffff][64] = {0}; int n = 0; float mintime=0.0f, maxtime=0.0f; char rev[10] = {0}; char line[128] = {0}; while (fgets(line,sizeof(line),f) && n < (sizeof(lines)/sizeof(*lines))) { replace(line,'\r','\0'); replace(line,'\n','\0'); if (strncmp(line,"HEAD is now at ", 15) == 0) { if (rev[0]) sprintf(lines[n++],"%s\t%.1f\t%.1f", rev, mintime, maxtime); strncpy(rev, line+15, 7); mintime = 0.0f; maxtime = 0.0f; } if (strncmp(line,"Overall time:",13)==0) { float time = atof(line+14); if (mintime < 0.1f || time < mintime) mintime = time; if (time > maxtime) maxtime = time; } } while (n > 0) printf("%s\n", lines[--n]); fclose(f); return 0; } cppcheck-1.82/tools/times.sh000077500000000000000000000022601322667425100160540ustar00rootroot00000000000000#!/bin/bash # # Simple script to generate times.log that contains timing information for the last revisions # Typically these commands shall be used to get times.txt: mkdir -p src || exit 1 cp lib/* src/ || exit 2 # fill src/ with some source code #NOTE: also try with some files other then from cppcheck! iterations=4 # if "old" already exists for some reason, do NOT work on current branch but bail out git checkout -b old || exit make clean git reset --hard HEAD > times.log for i in $(seq 1 50); do git_head=$(git log -1 --format=%h) # if build fails, make clean and try again make SRCDIR=build CXXFLAGS=-O2 -j4 || make clean ; make SRCDIR=build CXXFLAGS=-O2 -j4 echo "Run number $i" for j in $(seq 1 ${iterations}); do ./cppcheck --quiet --showtime=summary --enable=all --inconclusive src 2> /dev/null | tee -a times.log done grep "Overall" times.log | tail -${iterations} | sed s/s// | awk -v "i=$i" -v "iterations=$iterations" -v "git_head=$git_head" '{ sum+=$3} END {print "Run " i", "git_head " Average: " sum/iterations}' | tee -a times.log git reset --hard HEAD^1 | tee -a times.log done gcc -o tools/times tools/times.c tools/times cppcheck-1.82/tools/trac-keywords.py000066400000000000000000000015721322667425100175510ustar00rootroot00000000000000 import subprocess import sys TRACDB = 'trac.db' def readdb(): global TRACDB cmds = ['sqlite3', TRACDB, 'SELECT id,keywords FROM ticket WHERE status<>"closed";'] p = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() data = comm[0] ret = {} for line in data.splitlines(): pos1 = line.find('|') if pos1 <= 0: continue nr = line[:pos1] for kw in line[pos1+1:].split(' '): if kw == '': continue if kw not in ret: ret[kw] = [] ret[kw].append(nr); return ret for arg in sys.argv[1:]: if arg.endswith('/trac.db'): global TRACDB TRACDB = arg data = readdb() for kw in sorted(data.keys()): out = kw + ':' for ticket in data[kw]: out = out + ' ' + ticket print(out) cppcheck-1.82/tools/triage/000077500000000000000000000000001322667425100156475ustar00rootroot00000000000000cppcheck-1.82/tools/triage/triage/000077500000000000000000000000001322667425100171225ustar00rootroot00000000000000cppcheck-1.82/tools/triage/triage/codeeditor.cpp000066400000000000000000000054731322667425100217600ustar00rootroot00000000000000#include #include "codeeditor.h" CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent) { lineNumberArea = new LineNumberArea(this); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); updateLineNumberAreaWidth(0); highlightCurrentLine(); } 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) lineNumberArea->scroll(0, dy); else lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height()); if (rect.contains(viewport()->rect())) updateLineNumberAreaWidth(0); } void CodeEditor::resizeEvent(QResizeEvent *e) { QPlainTextEdit::resizeEvent(e); QRect cr = contentsRect(); lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); } void CodeEditor::highlightCurrentLine() { QList extraSelections; if (!isReadOnly()) { QTextEdit::ExtraSelection selection; QColor lineColor = QColor(Qt::yellow).lighter(160); selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = textCursor(); selection.cursor.clearSelection(); extraSelections.append(selection); } setExtraSelections(extraSelections); } void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) { QPainter painter(lineNumberArea); painter.fillRect(event->rect(), Qt::lightGray); 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(Qt::black); painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, number); } block = block.next(); top = bottom; bottom = top + (int) blockBoundingRect(block).height(); ++blockNumber; } } cppcheck-1.82/tools/triage/triage/codeeditor.h000066400000000000000000000020751322667425100214200ustar00rootroot00000000000000#ifndef CODEEDITOR_H #define CODEEDITOR_H #include #include class QPaintEvent; class QResizeEvent; class QSize; class QWidget; class LineNumberArea; class CodeEditor : public QPlainTextEdit { Q_OBJECT public: explicit CodeEditor(QWidget *parent = 0); void lineNumberAreaPaintEvent(QPaintEvent *event); int lineNumberAreaWidth(); protected: void resizeEvent(QResizeEvent *event) override; private slots: void updateLineNumberAreaWidth(int newBlockCount); void highlightCurrentLine(); void updateLineNumberArea(const QRect &, int); private: QWidget *lineNumberArea; }; class LineNumberArea : public QWidget { public: explicit LineNumberArea(CodeEditor *editor) : QWidget(editor) { codeEditor = editor; } QSize sizeHint() const override { return QSize(codeEditor->lineNumberAreaWidth(), 0); } protected: void paintEvent(QPaintEvent *event) override { codeEditor->lineNumberAreaPaintEvent(event); } private: CodeEditor *codeEditor; }; #endif // CODEEDITOR_H cppcheck-1.82/tools/triage/triage/main.cpp000066400000000000000000000002541322667425100205530ustar00rootroot00000000000000#include "mainwindow.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } cppcheck-1.82/tools/triage/triage/mainwindow.cpp000066400000000000000000000056631322667425100220140ustar00rootroot00000000000000#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include const QString WORK_FOLDER(QDir::homePath() + "/triage"); MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::loadFile() { const QString fileName = QFileDialog::getOpenFileName(this, tr("daca results file"), WORK_FOLDER, tr("Text files (*.txt)")); if (fileName.isEmpty()) return; ui->results->clear(); QFile file(fileName); file.open(QIODevice::ReadOnly | QIODevice::Text); QTextStream textStream(&file); QString url; while (true) { const QString line = textStream.readLine(); if (line.isNull()) break; if (line.startsWith("ftp://")) url = line; else if (!url.isEmpty()) { ui->results->addItem(url + "\n" + line); url.clear(); } } } void MainWindow::showResult(QListWidgetItem *item) { if (!item->text().startsWith("ftp://")) return; const QStringList lines = item->text().split("\n"); if (lines.size() != 2) return; const QString url = lines[0]; const QString msg = lines[1]; const QString archiveName = url.mid(url.lastIndexOf("/") + 1); const int pos1 = msg.indexOf(":"); const int pos2 = msg.indexOf(":", pos1+1); const QString fileName = msg.left(msg.indexOf(":")); const int lineNumber = msg.mid(pos1+1,pos2-pos1-1).toInt(); QProcess process; process.setWorkingDirectory(WORK_FOLDER); if (!QFileInfo(WORK_FOLDER + '/' + archiveName).exists()) { // Download archive process.start("wget", QStringList()<code->setFocus(); QFile f(WORK_FOLDER + '/' + fileName); f.open(QIODevice::ReadOnly | QIODevice::Text); QTextStream textStream(&f); const QString fileData = textStream.readAll(); ui->code->setPlainText(fileData); for (int pos = 0, line = 1; pos < fileData.size(); ++pos) { if (fileData[pos] == '\n') { ++line; if (line == lineNumber) { QTextCursor textCursor = ui->code->textCursor(); textCursor.setPosition(pos+1); ui->code->setTextCursor(textCursor); ui->code->centerCursor(); break; } } } } cppcheck-1.82/tools/triage/triage/mainwindow.h000066400000000000000000000006221322667425100214470ustar00rootroot00000000000000#ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void loadFile(); void showResult(QListWidgetItem *item); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H cppcheck-1.82/tools/triage/triage/mainwindow.ui000066400000000000000000000063641322667425100216460ustar00rootroot00000000000000 MainWindow 0 0 683 465 MainWindow Load Qt::Horizontal 40 20 false 0 0 683 25 TopToolBarArea false CodeEditor QPlainTextEdit
codeeditor.h
loadFile clicked() MainWindow pasteResults() 86 68 135 68 results itemDoubleClicked(QListWidgetItem*) MainWindow showResult(QListWidgetItem*) 28 104 5 104 loadFile clicked() MainWindow loadFile() 22 65 22 46 loadFile() showResult(QListWidgetItem*)
cppcheck-1.82/tools/triage/triage/triage.pro000066400000000000000000000020061322667425100211150ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2017-12-31T22:22:56 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = triage TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += main.cpp\ mainwindow.cpp \ codeeditor.cpp HEADERS += mainwindow.h \ codeeditor.h FORMS += mainwindow.ui cppcheck-1.82/triage/000077500000000000000000000000001322667425100145075ustar00rootroot00000000000000cppcheck-1.82/triage/linux-3.11/000077500000000000000000000000001322667425100162265ustar00rootroot00000000000000cppcheck-1.82/triage/linux-3.11/false-positives.txt000066400000000000000000000551411322667425100221120ustar00rootroot00000000000000[linux-3.11/arch/arm/mach-tegra/sleep.h:97]: (error) syntax error [linux-3.11/arch/frv/kernel/time.c:93]: (error) Uninitialized variable: day [linux-3.11/arch/frv/kernel/time.c:93]: (error) Uninitialized variable: hour [linux-3.11/arch/frv/kernel/time.c:93]: (error) Uninitialized variable: min [linux-3.11/arch/frv/kernel/time.c:93]: (error) Uninitialized variable: mon [linux-3.11/arch/frv/kernel/time.c:93]: (error) Uninitialized variable: sec [linux-3.11/arch/frv/kernel/time.c:93]: (error) Uninitialized variable: year [linux-3.11/arch/ia64/hp/common/sba_iommu.c:1144]: (error) Possible null pointer dereference: ioc [linux-3.11/arch/ia64/hp/common/sba_iommu.c:649]: (error) Possible null pointer dereference: ioc [linux-3.11/arch/powerpc/kvm/book3s_32_mmu.c:274]: (error) Array 'pteg[16]' accessed at index 17, which is out of bounds. [linux-3.11/arch/powerpc/kvm/book3s_32_mmu.c:277]: (error) Array 'pteg[16]' accessed at index 17, which is out of bounds. [linux-3.11/arch/powerpc/kvm/book3s_64_mmu.c:281]: (error) Array 'pteg[16]' accessed at index 17, which is out of bounds. [linux-3.11/arch/powerpc/kvm/book3s_64_mmu.c:285]: (error) Array 'pteg[16]' accessed at index 17, which is out of bounds. [linux-3.11/arch/powerpc/kvm/e500_mmu.c:361]: (error) Array 'vcpu_e500.gtlb_params[2]' accessed at index 2, which is out of bounds. [linux-3.11/arch/powerpc/kvm/e500_mmu.c:364]: (error) Array 'vcpu_e500.gtlb_nv[2]' accessed at index 2, which is out of bounds. [linux-3.11/arch/powerpc/platforms/powermac/low_i2c.c:1387]: (error) Array 'inst.buffer[64]' accessed at index 9998, which is out of bounds. [linux-3.11/arch/s390/kernel/ptrace.c:99]: (error) Memory leak: new.control [linux-3.11/arch/sh/drivers/push-switch.c:27]: (error) syntax error [linux-3.11/arch/sparc/kernel/pcic.c:694]: (error) Uninitialized variable: irq [linux-3.11/arch/x86/kernel/machine_kexec_32.c:115]: (error) Possible null pointer dereference: pmd [linux-3.11/drivers/block/rbd.c:1616]: (error) Possible null pointer dereference: rbd_dev [linux-3.11/drivers/crypto/nx/nx-842.c:1030]: (error) Possible null pointer dereference: node [linux-3.11/drivers/dma/mmp_pdma.c:461]: (error) Possible null pointer dereference: prev [linux-3.11/drivers/edac/i7core_edac.c:1427]: (error) Memory leak: i7core_dev [linux-3.11/drivers/edac/sb_edac.c:1198]: (error) Memory leak: sbridge_dev [linux-3.11/drivers/gpio/gpio-mcp23s08.c:616]: (error) Uninitialized variable: pdata [linux-3.11/drivers/gpu/drm/drm_ioc32.c:359]: (error) Uninitialized variable: s32 [linux-3.11/drivers/gpu/drm/i915/i915_irq.c:116]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/i915_irq.c:132]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/i915_irq.c:1918]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/i915_irq.c:3037]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/i915_irq.c:3248]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/i915_irq.c:3487]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/intel_ddi.c:983]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/intel_ddi.c:989]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/intel_display.c:9903]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/intel_pm.c:2459]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/intel_pm.c:2461]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/i915/intel_pm.c:4423]: (error) Uninitialized variable: pipe [linux-3.11/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c:186]: (error) Possible null pointer dereference: engine [linux-3.11/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c:189]: (error) Possible null pointer dereference: engine [linux-3.11/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c:190]: (error) Possible null pointer dereference: engine [linux-3.11/drivers/gpu/drm/radeon/radeon_atombios.c:2910]: (error) Uninitialized variable: args [linux-3.11/drivers/gpu/drm/radeon/radeon_atombios.c:2919]: (error) Uninitialized variable: args [linux-3.11/drivers/hid/hid-wiimote-debug.c:147]: (error) Array 'wiidebug_drmmap[13]' accessed at index 63, which is out of bounds. [linux-3.11/drivers/infiniband/hw/cxgb4/mem.c:532]: (error) Memory leak: page_list [linux-3.11/drivers/iommu/dmar.c:1019]: (error) Uninitialized variable: sts [linux-3.11/drivers/iommu/intel-iommu.c:1002]: (error) Uninitialized variable: val [linux-3.11/drivers/iommu/intel-iommu.c:1226]: (error) Uninitialized variable: sts [linux-3.11/drivers/iommu/intel-iommu.c:1243]: (error) Uninitialized variable: sts [linux-3.11/drivers/iommu/intel-iommu.c:984]: (error) Uninitialized variable: sts [linux-3.11/drivers/iommu/intel_irq_remapping.c:419]: (error) Uninitialized variable: sts [linux-3.11/drivers/iommu/omap-iommu.c:669]: (error) Possible null pointer dereference: fn [linux-3.11/drivers/isdn/gigaset/ev-layer.c:475]: (error) Array index -1 is out of bounds. [linux-3.11/drivers/isdn/i4l/isdn_ttyfax.c:329]: (error) Uninitialized variable: c [linux-3.11/drivers/isdn/icn/icn.c:1579]: (error) Memory leak: card [linux-3.11/drivers/isdn/isdnloop/isdnloop.c:1490]: (error) Memory leak: card [linux-3.11/drivers/macintosh/windfarm_pm121.c:540]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm121.c:553]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm121.c:557]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:294]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:295]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:296]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:297]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:300]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:301]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:302]: (error) Possible null pointer dereference: param [linux-3.11/drivers/macintosh/windfarm_pm81.c:305]: (error) Possible null pointer dereference: param [linux-3.11/drivers/md/dm-mpath.c:1590]: (error) Possible null pointer dereference: bdev [linux-3.11/drivers/md/dm-thin-metadata.c:1021]: (error) Memory leak: td [linux-3.11/drivers/md/dm-thin-metadata.c:1103]: (error) Memory leak: td [linux-3.11/drivers/md/dm-thin-metadata.c:988]: (error) Memory leak: td [linux-3.11/drivers/media/i2c/ir-kbd-i2c.c:219]: (error) Uninitialized variable: key [linux-3.11/drivers/media/platform/exynos4-is/fimc-capture.c:1224]: (error) Possible null pointer dereference: si [linux-3.11/drivers/media/platform/vino.c:3745]: (error) Array 'vino_indycam_v4l2_controls[9]' accessed at index 9, which is out of bounds. [linux-3.11/drivers/media/platform/vino.c:3766]: (error) Array 'vino_saa7191_v4l2_controls[9]' accessed at index 9, which is out of bounds. [linux-3.11/drivers/media/usb/gspca/gspca.c:1608]: (error) Possible null pointer dereference: frame [linux-3.11/drivers/misc/fsa9480.c:254]: (error) syntax error [linux-3.11/drivers/misc/sgi-xp/xpc_partition.c:426]: (error) Memory leak: remote_rp_base [linux-3.11/drivers/mmc/host/rtsx_pci_sdmmc.c:753]: (error) Uninitialized variable: width [linux-3.11/drivers/net/bonding/bond_main.c:4618]: (error) Array 'arp_ip_target[16]' accessed at index 9998, which is out of bounds. [linux-3.11/drivers/net/bonding/bond_main.c:4619]: (error) Array 'arp_ip_target[16]' accessed at index 9998, which is out of bounds. [linux-3.11/drivers/net/can/sja1000/peak_pci.c:690]: (error) Possible null pointer dereference: chan [linux-3.11/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c:683]: (error) Memory leak: diff.hi [linux-3.11/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c:683]: (error) Memory leak: diff.lo [linux-3.11/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c:903]: (error) Memory leak: diff.hi [linux-3.11/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c:903]: (error) Memory leak: diff.lo [linux-3.11/drivers/net/ethernet/dec/tulip/de4x5.c:5011]: (error) Array 'lp.phy[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/net/ethernet/dec/tulip/de4x5.c:5012]: (error) Array 'lp.phy[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/net/ethernet/dec/tulip/de4x5.c:5013]: (error) Array 'lp.phy[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/net/ethernet/dec/tulip/de4x5.c:5014]: (error) Array 'lp.phy[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/net/ethernet/dec/tulip/de4x5.c:5015]: (error) Array 'lp.phy[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/net/ethernet/emulex/benet/be_cmds.c:363]: (error) Uninitialized variable: compl [linux-3.11/drivers/net/ethernet/intel/e1000/e1000_hw.c:5617]: (error) Uninitialized variable: phy_data [linux-3.11/drivers/net/ethernet/intel/e1000/e1000_hw.c:5664]: (error) Uninitialized variable: phy_data [linux-3.11/drivers/net/ethernet/marvell/sky2.c:1173]: (error) Uninitialized variable: re [linux-3.11/drivers/net/wireless/airo.c:7646]: (error) syntax error [linux-3.11/drivers/net/wireless/ath/ath10k/core.c:132]: (error) Uninitialized variable: ret [linux-3.11/drivers/net/wireless/ath/ath10k/core.c:212]: (error) Uninitialized variable: ret [linux-3.11/drivers/net/wireless/ath/ath10k/core.c:391]: (error) Uninitialized variable: ret [linux-3.11/drivers/net/wireless/ath/ath10k/pci.c:816]: (error) Memory leak: compl [linux-3.11/drivers/net/wireless/hostap/hostap_ap.c:3221]: (error) Memory leak: sta [linux-3.11/drivers/net/wireless/orinoco/fw.c:144]: (error) Possible null pointer dereference: fw_entry [linux-3.11/drivers/net/wireless/orinoco/fw.c:303]: (error) Possible null pointer dereference: fw_entry [linux-3.11/drivers/net/wireless/orinoco/fw.c:304]: (error) Possible null pointer dereference: fw_entry [linux-3.11/drivers/net/wireless/orinoco/fw.c:322]: (error) Possible null pointer dereference: fw_entry [linux-3.11/drivers/net/wireless/orinoco/fw.c:323]: (error) Possible null pointer dereference: fw_entry [linux-3.11/drivers/net/wireless/ti/wl18xx/debugfs.c:118]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/net/wireless/ti/wl18xx/debugfs.c:142]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/net/wireless/ti/wl18xx/debugfs.c:144]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/net/wireless/ti/wl18xx/debugfs.c:165]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/s390/block/scm_blk.c:85]: (error) Memory leak: aobrq [linux-3.11/drivers/sbus/char/envctrl.c:385]: (error) Array 'pchild.chnl_array[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/sbus/char/envctrl.c:388]: (error) Array 'pchild.tblprop_array[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/sbus/char/envctrl.c:390]: (error) Array 'pchild.tblprop_array[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/sbus/char/envctrl.c:391]: (error) Array 'pchild.tblprop_array[8]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c:649]: (error) Possible null pointer dereference: tab_str [linux-3.11/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c:649]: (error) Possible null pointer dereference: tab_str2 [linux-3.11/drivers/scsi/be2iscsi/be_cmds.c:412]: (error) Uninitialized variable: compl [linux-3.11/drivers/scsi/device_handler/scsi_dh.c:417]: (error) Possible null pointer dereference: scsi_dh [linux-3.11/drivers/scsi/FlashPoint.c:5464]: (error) Array 'FPT_BL_Card[8]' accessed at index 9, which is out of bounds. [linux-3.11/drivers/scsi/gdth.c:730]: (error) Array 'irq[16]' accessed at index 16, which is out of bounds. [linux-3.11/drivers/scsi/gdth.c:731]: (error) Array 'irq[16]' accessed at index 16, which is out of bounds. [linux-3.11/drivers/scsi/libsas/sas_init.c:317]: (error) Uninitialized variable: ret [linux-3.11/drivers/scsi/libsas/sas_init.c:346]: (error) Uninitialized variable: ret [linux-3.11/drivers/scsi/libsas/sas_init.c:385]: (error) Uninitialized variable: ret [linux-3.11/drivers/scsi/lpfc/lpfc_bsg.c:3157]: (error) Possible null pointer dereference: rsp [linux-3.11/drivers/scsi/lpfc/lpfc_els.c:3455]: (error) Possible null pointer dereference: buf_ptr [linux-3.11/drivers/scsi/lpfc/lpfc_init.c:1103]: (error) Possible null pointer dereference: buf_ptr [linux-3.11/drivers/scsi/lpfc/lpfc_init.c:3036]: (error) Possible null pointer dereference: psb [linux-3.11/drivers/scsi/lpfc/lpfc_init.c:3037]: (error) Possible null pointer dereference: psb [linux-3.11/drivers/scsi/lpfc/lpfc_scsi.c:1261]: (error) Possible null pointer dereference: psb [linux-3.11/drivers/scsi/lpfc/lpfc_scsi.c:1262]: (error) Possible null pointer dereference: psb [linux-3.11/drivers/scsi/lpfc/lpfc_scsi.c:1263]: (error) Possible null pointer dereference: psb [linux-3.11/drivers/scsi/lpfc/lpfc_scsi.c:1265]: (error) Possible null pointer dereference: psb [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:1129]: (error) Possible null pointer dereference: piocb [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:12094]: (error) Possible null pointer dereference: dmabuf [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:16245]: (error) Possible null pointer dereference: mb [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:1894]: (error) Possible null pointer dereference: hbq_buffer [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:9016]: (error) Possible null pointer dereference: pmb [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:9017]: (error) Possible null pointer dereference: pmb [linux-3.11/drivers/scsi/lpfc/lpfc_sli.c:9155]: (error) Possible null pointer dereference: buf_ptr [linux-3.11/drivers/scsi/mpt2sas/mpt2sas_ctl.c:1045]: (error) Buffer is accessed out of bounds. [linux-3.11/drivers/scsi/mpt2sas/mpt2sas_ctl.c:1046]: (error) Buffer is accessed out of bounds. [linux-3.11/drivers/scsi/mpt3sas/mpt3sas_ctl.c:1038]: (error) Buffer is accessed out of bounds. [linux-3.11/drivers/scsi/mpt3sas/mpt3sas_ctl.c:1039]: (error) Buffer is accessed out of bounds. [linux-3.11/drivers/spi/spi-s3c24xx.c:338]: (error) Possible null pointer dereference: code [linux-3.11/drivers/staging/bcm/PHSModule.c:904]: (error) Array 'psServiceFlowTable.stSFList[17]' accessed at index 17, which is out of bounds. [linux-3.11/drivers/staging/bcm/PHSModule.c:909]: (error) Array 'psServiceFlowTable.stSFList[17]' accessed at index 17, which is out of bounds. [linux-3.11/drivers/staging/bcm/PHSModule.c:910]: (error) Array 'psServiceFlowTable.stSFList[17]' accessed at index 17, which is out of bounds. [linux-3.11/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c:2256]: (error) Possible null pointer dereference: net [linux-3.11/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c:2269]: (error) Possible null pointer dereference: net [linux-3.11/drivers/staging/lustre/lustre/fld/fld_index.c:357]: (error) Possible null pointer dereference: dt_obj [linux-3.11/drivers/staging/lustre/lustre/obdclass/cl_lock.c:2171]: (error) Possible null pointer dereference: lock [linux-3.11/drivers/staging/lustre/lustre/obdclass/cl_lock.c:603]: (error) Possible null pointer dereference: lock [linux-3.11/drivers/staging/lustre/lustre/obdclass/cl_lock.c:758]: (error) Possible null pointer dereference: lock [linux-3.11/drivers/staging/lustre/lustre/obdclass/cl_lock.c:854]: (error) Possible null pointer dereference: lock [linux-3.11/drivers/staging/lustre/lustre/obdclass/cl_lock.c:873]: (error) Possible null pointer dereference: lock [linux-3.11/drivers/staging/lustre/lustre/obdclass/llog_cat.c:204]: (error) Possible null pointer dereference: loghandle [linux-3.11/drivers/staging/lustre/lustre/obdclass/llog_cat.c:205]: (error) Possible null pointer dereference: loghandle [linux-3.11/drivers/staging/lustre/lustre/obdclass/llog_cat.c:206]: (error) Possible null pointer dereference: loghandle [linux-3.11/drivers/staging/lustre/lustre/obdclass/llog_cat.c:207]: (error) Possible null pointer dereference: loghandle [linux-3.11/drivers/tty/isicom.c:1581]: (error) Possible null pointer dereference: board [linux-3.11/drivers/tty/isicom.c:1582]: (error) Possible null pointer dereference: board [linux-3.11/drivers/tty/isicom.c:1583]: (error) Possible null pointer dereference: board [linux-3.11/drivers/tty/tty_io.c:1450]: (error) Possible null pointer dereference: driver [linux-3.11/drivers/usb/core/file.c:213]: (error) Array 'usb_minors[256]' accessed at index 256, which is out of bounds. [linux-3.11/drivers/usb/gadget/dummy_hcd.c:277]: (error) Uninitialized variable: ep [linux-3.11/drivers/usb/gadget/ether.c:376]: (error) Uninitialized variable: net [linux-3.11/drivers/usb/gadget/ether.c:377]: (error) Uninitialized variable: net [linux-3.11/drivers/usb/gadget/f_fs.c:1937]: (error) Memory leak: d [linux-3.11/drivers/usb/gadget/fsl_udc_core.c:855]: (error) Possible null pointer dereference: last_dtd [linux-3.11/drivers/usb/gadget/fsl_udc_core.c:856]: (error) Possible null pointer dereference: last_dtd [linux-3.11/drivers/usb/gadget/lpc32xx_udc.c:557]: (error) Uninitialized variable: ep [linux-3.11/drivers/usb/gadget/mv_udc_core.c:434]: (error) Possible null pointer dereference: last_dtd [linux-3.11/drivers/usb/gadget/mv_udc_core.c:435]: (error) Possible null pointer dereference: last_dtd [linux-3.11/drivers/usb/gadget/omap_udc.c:1359]: (error) Uninitialized variable: ep [linux-3.11/drivers/usb/gadget/u_serial.c:1135]: (error) Array 'ports[4]' accessed at index 4, which is out of bounds. [linux-3.11/drivers/usb/gadget/u_serial.c:1136]: (error) Array 'ports[4]' accessed at index 4, which is out of bounds. [linux-3.11/drivers/usb/host/isp1362.h:932]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:1048]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:1057]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:1071]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:1102]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:1124]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:435]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:441]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:698]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:706]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/host/isp1362-hcd.c:714]: (error) Uninitialized variable: __v [linux-3.11/drivers/usb/musb/cppi_dma.c:852]: (error) Possible null pointer dereference: tail [linux-3.11/drivers/usb/musb/cppi_dma.c:853]: (error) Possible null pointer dereference: tail [linux-3.11/drivers/usb/serial/mos7840.c:1472]: (error) Array 'mos7840_port.busy[16]' accessed at index 16, which is out of bounds. [linux-3.11/drivers/video/matrox/matroxfb_maven.c:159]: (error) Uninitialized variable: dst [linux-3.11/drivers/video/sm501fb.c:1768]: (error) Possible null pointer dereference: pd [linux-3.11/fs/adfs/dir.c:181]: (error) Deallocation of an auto-variable results in undefined behaviour. [linux-3.11/fs/adfs/dir.c:63]: (error) Deallocation of an auto-variable results in undefined behaviour. [linux-3.11/fs/adfs/dir.c:97]: (error) Deallocation of an auto-variable results in undefined behaviour. [linux-3.11/fs/bio.c:147]: (error) Possible null pointer dereference: bslab [linux-3.11/fs/btrfs/send.c:1586]: (error) Uninitialized struct member: found_key.objectid [linux-3.11/fs/btrfs/send.c:1587]: (error) Uninitialized struct member: found_key.type [linux-3.11/fs/cifs/connect.c:3420]: (error) Uninitialized variable: tcon [linux-3.11/fs/cifs/connect.c:3422]: (error) Uninitialized variable: ses [linux-3.11/fs/cifs/connect.c:3425]: (error) Uninitialized variable: xid [linux-3.11/fs/exofs/ore.c:199]: (error) Memory leak: extra_part [linux-3.11/fs/exofs/super.c:570]: (error) Memory leak: aoded [linux-3.11/fs/nfsd/nfs4recover.c:1079]: (error) Null pointer dereference [linux-3.11/fs/nfsd/nfs4recover.c:1108]: (error) Null pointer dereference [linux-3.11/fs/ntfs/compress.c:228]: (error) Uninitialized variable: completed_pages [linux-3.11/fs/ocfs2/dlm/dlmrecovery.c:1820]: (error) Possible null pointer dereference: lock [linux-3.11/fs/ubifs/key.h:542]: (error) Signed integer overflow for expression '536870912)*4096' [linux-3.11/fs/xfs/xfs_btree.c:3297]: (error) Array 'cur.bc_ptrs[8]' accessed at index 9998, which is out of bounds. [linux-3.11/fs/xfs/xfs_dir2_node.c:1502]: (error) Possible null pointer dereference: bp [linux-3.11/kernel/cgroup.c:4908]: (error) Uninitialized variable: ss [linux-3.11/kernel/cgroup.c:4933]: (error) Uninitialized variable: ss [linux-3.11/kernel/cgroup.c:5340]: (error) Uninitialized variable: ss [linux-3.11/kernel/sched/rt.c:607]: (error) Uninitialized variable: iter [linux-3.11/kernel/sched/rt.c:688]: (error) Uninitialized variable: iter [linux-3.11/kernel/trace/ftrace.c:184]: (error) Boolean value assigned to pointer. [linux-3.11/kernel/trace/ftrace.c:4313]: (error) Boolean value assigned to pointer. [linux-3.11/kernel/trace/ftrace.c:4346]: (error) Boolean value assigned to pointer. [linux-3.11/mm/frontswap.c:335]: (error) Possible null pointer dereference: si [linux-3.11/mm/frontswap.c:353]: (error) Possible null pointer dereference: si [linux-3.11/mm/mlock.c:500]: (error) Possible null pointer dereference: prev [linux-3.11/mm/slab.c:2709]: (error) Uninitialized variable: slabp [linux-3.11/net/ax25/ax25_dev.c:141]: (error) Uninitialized variable: ax25_dev [linux-3.11/net/ax25/ax25_dev.c:37]: (error) Uninitialized variable: ax25_dev [linux-3.11/net/ceph/mon_client.c:65]: (error) Possible null pointer dereference: m [linux-3.11/net/core/skbuff.c:2832]: (error) Possible null pointer dereference: tail [linux-3.11/net/ipv4/devinet.c:1983]: (error) Uninitialized variable: ifindex [linux-3.11/net/ipv4/ipconfig.c:276]: (error) syntax error [linux-3.11/net/sctp/sm_make_chunk.c:1447]: (error) Possible null pointer dereference: data [linux-3.11/scripts/kconfig/confdata.c:970]: (error) Resource leak: tristate [linux-3.11/sound/pci/pcxhr/pcxhr.c:729]: (error) Uninitialized struct member: pipe.first_audio [linux-3.11/tools/hv/hv_kvp_daemon.c:292]: (error) Common realloc mistake: 'record' nulled but not freed upon failure [linux-3.11/tools/hv/hv_kvp_daemon.c:395]: (error) Common realloc mistake: 'record' nulled but not freed upon failure [linux-3.11/tools/perf/builtin-kmem.c:370]: (error) Uninitialized struct member: map.unmap_ip [linux-3.11/tools/perf/builtin-kmem.c:370]: (error) Uninitialized variable: map [linux-3.11/virt/kvm/kvm_main.c:2305]: (error) Possible null pointer dereference: ops cppcheck-1.82/triage/linux-3.11/linux-3.11.h000066400000000000000000000013151322667425100201160ustar00rootroot00000000000000 #define __alignof__(x) 1 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0])) #define BUG_ON(C) if (C) exit(1) #define GOTO(label, rc) goto label #define RETURN(x) return x #define NVERSION(version) (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define __get_user(x, ptr) ((x) = somevalue + sizeof(*ptr)) void panic(const char *fmt, ...) __attribute__((noreturn)); cppcheck-1.82/triage/linux-3.11/runcppcheck.cmd000077500000000000000000000001641322667425100212240ustar00rootroot00000000000000# command used to generate error report for linux-3.11 cppcheck --include=linux-3.11.h --platform=unix64 linux-3.11 cppcheck-1.82/triage/linux-3.11/true-positives.txt000066400000000000000000001102071322667425100217720ustar00rootroot00000000000000[linux-3.11/arch/arm64/kvm/sys_regs.c:1020]: (error) Uninitialized variable: clidr [linux-3.11/arch/arm64/kvm/sys_regs.c:1021]: (error) Uninitialized struct member: clidr.val [linux-3.11/arch/cris/arch-v32/drivers/sync_serial.c:285]: (error) Array 'ports[1]' accessed at index 1, which is out of bounds. [linux-3.11/arch/ia64/hp/common/sba_iommu.c:1003]: (error) Possible null pointer dereference: ioc [linux-3.11/arch/ia64/hp/common/sba_iommu.c:1502]: (error) Possible null pointer dereference: ioc [linux-3.11/arch/ia64/hp/common/sba_iommu.c:956]: (error) Possible null pointer dereference: ioc [linux-3.11/arch/ia64/kernel/palinfo.c:133]: (error) Array 'units[5]' accessed at index 6, which is out of bounds. [linux-3.11/arch/ia64/sn/pci/tioca_provider.c:484] -> [linux-3.11/arch/ia64/sn/pci/tioca_provider.c:487]: (warning) Possible null pointer dereference: map - otherwise it is redundant to check it against null. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: ''. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_CRAMFS;CONFIG_MTD_UCLINUX'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_DEBUG_FS'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_DEBUG_FS;CONFIG_MMU'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_DUMMY_CONSOLE;CONFIG_VT'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_EARLY_PRINTK'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_MTD_UCLINUX'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_MTD_UCLINUX;CONFIG_ROMFS_FS'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_VT'. [linux-3.11/arch/microblaze/kernel/setup.c:180]: (error) Invalid number of character (() when these macros are defined: 'CONFIG_VT;CONFIG_XILINX_CONSOLE'. [linux-3.11/arch/mips/ralink/prom.c:64]: (error) Uninitialized variable: argc [linux-3.11/arch/mips/txx9/generic/pci.c:334]: (error) Array 'regs[2]' accessed at index 2, which is out of bounds. [linux-3.11/arch/mn10300/boot/tools/build.c:54]: (error) va_list 'args' was opened but not closed by va_end(). [linux-3.11/arch/powerpc/kernel/cacheinfo.c:384] -> [linux-3.11/arch/powerpc/kernel/cacheinfo.c:386]: (warning) Possible null pointer dereference: cache - otherwise it is redundant to check it against null. [linux-3.11/arch/powerpc/kernel/prom_init.c:391]: (error) va_list 'args' was opened but not closed by va_end(). [linux-3.11/arch/powerpc/perf/power4-pmu.c:506]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/arch/powerpc/perf/ppc970-pmu.c:380]: (error) Array 'pmcsel[8]' accessed at index 8, which is out of bounds. [linux-3.11/arch/powerpc/platforms/powermac/bootx_init.c:87]: (error) va_list 'args' was opened but not closed by va_end(). [linux-3.11/arch/um/drivers/net_user.c:258]: (error) va_list 'ap' was opened but not closed by va_end(). [linux-3.11/arch/unicore32/kernel/early_printk.c:48]: (error) Uninitialized variable: keep_early [linux-3.11/arch/x86/boot/compressed/mkpiggy.c:61]: (error) Resource leak: f [linux-3.11/arch/x86/boot/tools/build.c:135]: (error) va_list 'args' was opened but not closed by va_end(). [linux-3.11/arch/x86/kvm/x86.c:5471]: (error) Uninitialized variable: param [linux-3.11/arch/x86/kvm/x86.c:5472]: (error) Uninitialized variable: param [linux-3.11/arch/x86/kvm/x86.c:5473]: (error) Uninitialized variable: param [linux-3.11/arch/x86/kvm/x86.c:5474]: (error) Uninitialized variable: param [linux-3.11/arch/x86/math-emu/poly_2xm1.c:127]: (error) Uninitialized struct member: Denom.msw [linux-3.11/arch/x86/math-emu/poly_2xm1.c:73]: (error) Uninitialized struct member: argSignif.msw [linux-3.11/arch/x86/math-emu/poly_atan.c:92]: (error) Uninitialized variable: argSignif [linux-3.11/arch/x86/math-emu/poly_l2.c:194]: (error) Uninitialized variable: argSignif [linux-3.11/arch/x86/math-emu/poly_l2.c:199]: (error) Uninitialized variable: argSignif [linux-3.11/arch/x86/math-emu/poly_l2.c:205]: (error) Uninitialized struct member: Numer.msw [linux-3.11/arch/x86/math-emu/poly_l2.c:212]: (error) Uninitialized struct member: argSignif.msw [linux-3.11/arch/x86/math-emu/poly_l2.c:218]: (error) Uninitialized struct member: argSignif.lsw [linux-3.11/arch/x86/math-emu/poly_l2.c:219]: (error) Uninitialized struct member: argSignif.midw [linux-3.11/arch/x86/math-emu/poly_sin.c:352]: (error) Uninitialized struct member: fix_up.msw [linux-3.11/arch/x86/math-emu/poly_tan.c:108]: (error) Uninitialized struct member: accum.lsw [linux-3.11/arch/x86/math-emu/poly_tan.c:186]: (error) Uninitialized struct member: fix_up.msw [linux-3.11/arch/xtensa/platforms/iss/network.c:115]: (error) va_list 'ap' was opened but not closed by va_end(). [linux-3.11/Documentation/connector/ucon.c:250]: (error) Resource leak: out [linux-3.11/Documentation/laptops/hpfall.c:33]: (error) Dangerous usage of 'devname' (strncpy doesn't always null-terminate it). [linux-3.11/drivers/acpi/battery.c:553] -> [linux-3.11/drivers/acpi/battery.c:557]: (performance) Variable 'status' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/bus.c:737] -> [linux-3.11/drivers/acpi/bus.c:740]: (performance) Variable 'status' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/glue.c:243] -> [linux-3.11/drivers/acpi/glue.c:245]: (performance) Variable 'retval' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/power.c:264] -> [linux-3.11/drivers/acpi/power.c:266]: (performance) Variable 'status' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/power.c:611] -> [linux-3.11/drivers/acpi/power.c:630]: (performance) Variable 'status' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/processor_core.c:170] -> [linux-3.11/drivers/acpi/processor_core.c:172]: (performance) Variable 'apic_id' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/processor_perflib.c:365] -> [linux-3.11/drivers/acpi/processor_perflib.c:374]: (performance) Variable 'status' is reassigned a value before the old one has been used. [linux-3.11/drivers/acpi/processor_perflib.c:577] -> [linux-3.11/drivers/acpi/processor_perflib.c:584]: (performance) Variable 'status' is reassigned a value before the old one has been used. [linux-3.11/drivers/atm/fore200e.c:2802]: (error) Uninitialized variable: err [linux-3.11/drivers/char/tile-srom.c:156]: (error) Uninitialized variable: dummy [linux-3.11/drivers/clk/sunxi/clk-sunxi.c:52]: (error) Memory leak: fixed [linux-3.11/drivers/clk/sunxi/clk-sunxi.c:52]: (error) Memory leak: gate [linux-3.11/drivers/cpufreq/cpufreq.c:341] -> [linux-3.11/drivers/cpufreq/cpufreq.c:353]: (warning) Possible null pointer dereference: policy - otherwise it is redundant to check it against null. [linux-3.11/drivers/crypto/caam/caamhash.c:822]: (error) Uninitialized variable: desc [linux-3.11/drivers/dma/mmp_tdma.c:190]: (error) Uninitialized variable: tdcr [linux-3.11/drivers/dma/mmp_tdma.c:231]: (error) Uninitialized variable: tdcr [linux-3.11/drivers/dma/txx9dmac.c:1227] -> [linux-3.11/drivers/dma/txx9dmac.c:1247]: (warning) Possible null pointer dereference: pdata - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-twl4030.c:530] -> [linux-3.11/drivers/gpio/gpio-twl4030.c:543]: (warning) Possible null pointer dereference: pdata - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:60] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:61] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:63] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:64] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:65] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:66] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpio/gpio-ucb1400.c:67] -> [linux-3.11/drivers/gpio/gpio-ucb1400.c:73]: (warning) Possible null pointer dereference: ucb - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c:219]: (error) Array 'dsi_errors[31]' accessed at index 31, which is out of bounds. [linux-3.11/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c:536] -> [linux-3.11/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c:541]: (warning) Possible null pointer dereference: sender - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpu/drm/i915/i915_gem.c:986]: (error) Possible null pointer dereference: ring [linux-3.11/drivers/gpu/drm/i915/i915_gem_context.c:312] -> [linux-3.11/drivers/gpu/drm/i915/i915_gem_context.c:321]: (warning) Possible null pointer dereference: file - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpu/drm/i915/intel_overlay.c:687] -> [linux-3.11/drivers/gpu/drm/i915/intel_overlay.c:692]: (warning) Possible null pointer dereference: overlay - otherwise it is redundant to check it against null. [linux-3.11/drivers/gpu/drm/qxl/qxl_draw.c:352]: (error) Uninitialized variable: drawable [linux-3.11/drivers/gpu/drm/radeon/radeon_legacy_tv.c:652]: (error) Array 'SLOPE_value[5]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/gpu/drm/radeon/radeon_legacy_tv.c:653]: (error) Array 'SLOPE_value[5]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/gpu/drm/radeon/radeon_legacy_tv.c:656]: (error) Array 'YCOEF_EN_value[5]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/gpu/drm/radeon/radeon_legacy_tv.c:656]: (error) Array 'YCOEF_value[5]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/gpu/drm/radeon/radeon_legacy_tv.c:657]: (error) Array 'SLOPE_value[5]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/gpu/drm/radeon/radeon_legacy_tv.c:660]: (error) Array 'SLOPE_value[5]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/infiniband/hw/ipath/ipath_diag.c:366]: (error) Uninitialized struct member: dp.unit [linux-3.11/drivers/input/touchscreen/ad7879-spi.c:37]: (error) Uninitialized variable: _rx_buf [linux-3.11/drivers/isdn/mISDN/dsp_cmx.c:1623]: (error) Invalid number of character ({) when these macros are defined: 'DSP_NEVER_DEFINED'. [linux-3.11/drivers/media/dvb-frontends/dib9000.c:1047]: (error) Array 'mb[10]' accessed at index 10, which is out of bounds. [linux-3.11/drivers/media/dvb-frontends/ds3000.c:619]: (error) Array 'dvbs2_snr_tab[80]' accessed at index 80, which is out of bounds. [linux-3.11/drivers/media/dvb-frontends/stv0367.c:926]: (error) Possible null pointer dereference: tuner_ops [linux-3.11/drivers/media/i2c/ov9650.c:710]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/media/i2c/ov9650.c:711]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/media/i2c/ov9650.c:725]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/media/pci/saa7134/saa7134-input.c:136]: (error) Uninitialized variable: b [linux-3.11/drivers/media/usb/dvb-usb-v2/mxl111sf.c:139]: (error) Uninitialized variable: val [linux-3.11/drivers/mfd/pm8921-core.c:178]: (error) Possible null pointer dereference: pmic [linux-3.11/drivers/mfd/tps65911-comparator.c:61]: (error) Array 'tps_comparators[2]' accessed at index 2, which is out of bounds. [linux-3.11/drivers/mfd/tps65911-comparator.c:88]: (error) Array 'tps_comparators[2]' accessed at index 2, which is out of bounds. [linux-3.11/drivers/misc/lkdtm.c:330]: (error) Uninitialized variable: data [linux-3.11/drivers/misc/sgi-xp/xpc_partition.c:73]: (error) Uninitialized variable: buf [linux-3.11/drivers/misc/sgi-xp/xpc_sn2.c:2173]: (error) Uninitialized variable: msg [linux-3.11/drivers/misc/sgi-xp/xpc_sn2.c:2174]: (error) Uninitialized variable: notify [linux-3.11/drivers/mmc/host/sdhci-spear.c:93]: (error) Possible null pointer dereference: pdata [linux-3.11/drivers/net/ethernet/emulex/benet/be_cmds.c:1542]: (error) Uninitialized variable: status [linux-3.11/drivers/net/ethernet/i825xx/sun3_82586.c:993]: (error) Array 'p.xmit_cmds[1]' accessed at index 1, which is out of bounds. [linux-3.11/drivers/net/ethernet/marvell/mvneta.c:2779]: (error) Uninitialized variable: phy_addr [linux-3.11/drivers/net/ethernet/smsc/smc91x.c:556]: (error) Uninitialized variable: flags [linux-3.11/drivers/net/fddi/skfp/ecm.c:500]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/net/wireless/ath/ath9k/eeprom.c:414]: (error) Array 'data_9287[idxL].pwrPdg[4][1]' index data_9287[idxL].pwrPdg[0][4] out of bounds. [linux-3.11/drivers/net/wireless/ath/ath9k/hw.c:1280]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/net/wireless/ath/ath9k/hw.c:1282]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1851]: (error) Uninitialized struct member: gains.dac [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1851]: (error) Uninitialized struct member: gains.gm [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1851]: (error) Uninitialized struct member: gains.pad [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1851]: (error) Uninitialized struct member: gains.pga [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1851]: (error) Uninitialized variable: gains [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1853]: (error) Uninitialized struct member: gains.dac [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1853]: (error) Uninitialized struct member: gains.gm [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1853]: (error) Uninitialized struct member: gains.pad [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1853]: (error) Uninitialized struct member: gains.pga [linux-3.11/drivers/net/wireless/b43/phy_lp.c:1853]: (error) Uninitialized variable: gains [linux-3.11/drivers/net/wireless/b43/phy_n.c:167] -> [linux-3.11/drivers/net/wireless/b43/phy_n.c:171]: (warning) Possible null pointer dereference: e - otherwise it is redundant to check it against null. [linux-3.11/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c:133]: (error) Uninitialized variable: err_ret [linux-3.11/drivers/net/wireless/libertas/cmd.c:1129]: (error) Uninitialized struct member: cmd.control [linux-3.11/drivers/net/wireless/libertas/cmd.c:1131]: (error) Uninitialized struct member: cmd.control [linux-3.11/drivers/net/wireless/rt2x00/rt2x00mac.c:799]: (error) Uninitialized struct member: setup.rx_chain_num [linux-3.11/drivers/net/wireless/rt2x00/rt2x00mac.c:799]: (error) Uninitialized struct member: setup.tx_chain_num [linux-3.11/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c:321]: (error) Uninitialized variable: packet_beacon [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:559]: (error) Uninitialized variable: value8 [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:560]: (error) Uninitialized variable: value32 [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:566]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:570]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:574]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:575]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:579]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:591]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/hw.c:598]: (error) Uninitialized variable: txpktbuf_bndy [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192de/phy.c:217]: (error) Uninitialized variable: dbi_direct [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192se/rf.c:66]: (error) Uninitialized variable: pwrbase0 [linux-3.11/drivers/net/wireless/rtlwifi/rtl8192se/rf.c:67]: (error) Uninitialized variable: pwrbase0 [linux-3.11/drivers/net/wireless/ti/wlcore/boot.c:126]: (error) Undefined behavior: Variable 'min_fw_str' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/net/wireless/ti/wlcore/boot.c:129]: (error) Undefined behavior: Variable 'min_fw_str' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/platform/x86/sony-laptop.c:795]: (error) Uninitialized variable: len [linux-3.11/drivers/s390/char/con3215.c:733]: (error) Array 'raw3215[1]' accessed at index 1, which is out of bounds. [linux-3.11/drivers/scsi/aacraid/rx.c:503]: (error) Uninitialized variable: var [linux-3.11/drivers/scsi/advansys.c:11505]: (error) Uninitialized variable: ret [linux-3.11/drivers/scsi/advansys.c:11560]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11563]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11564]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11565]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11566]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11567]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11568]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11569]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11570]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11571]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11572]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11573]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11575]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11577]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11579]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11581]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11582]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11586]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11587]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11588]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11590]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11592]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11593]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11594]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11595]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11596]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11597]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11598]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11599]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11600]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11601]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11603]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11605]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11607]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11609]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11614]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11615]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11616]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11618]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11620]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11621]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11622]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11623]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11624]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11625]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11626]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11627]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11628]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11629]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11631]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11633]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11635]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11637]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11677]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11680]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11741]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11743]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11745]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/advansys.c:11747]: (error) Possible null pointer dereference: adv_dvc_varp [linux-3.11/drivers/scsi/bfa/bfa_fcs_lport.c:5808] -> [linux-3.11/drivers/scsi/bfa/bfa_fcs_lport.c:5811]: (warning) Possible null pointer dereference: port - otherwise it is redundant to check it against null. [linux-3.11/drivers/scsi/bfa/bfa_svc.c:1026] -> [linux-3.11/drivers/scsi/bfa/bfa_svc.c:1028]: (warning) Possible null pointer dereference: fcxp - otherwise it is redundant to check it against null. [linux-3.11/drivers/scsi/BusLogic.c:699]: (error) Uninitialized variable: adapter [linux-3.11/drivers/scsi/csiostor/csio_lnode.c:873] -> [linux-3.11/drivers/scsi/csiostor/csio_lnode.c:878]: (warning) Possible null pointer dereference: ln - otherwise it is redundant to check it against null. [linux-3.11/drivers/scsi/eata_pio.c:923]: (error) Array 'reg_IRQ[16]' accessed at index 16, which is out of bounds. [linux-3.11/drivers/scsi/pm8001/pm80xx_hwi.c:811]: (error) Uninitialized variable: ret [linux-3.11/drivers/scsi/qla2xxx/qla_attr.c:896]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/spi/spi-dw.c:659]: (error) Memory leak: chip [linux-3.11/drivers/staging/bcm/CmHost.c:1294]: (error) Array 'psfCSType.cCPacketClassificationRule.u8IPv6FlowLable[3]' accessed at index 3, which is out of bounds. [linux-3.11/drivers/staging/bcm/CmHost.c:1294]: (error) Array 'psfCSType.cCPacketClassificationRule.u8IPv6FlowLable[3]' accessed at index 4, which is out of bounds. [linux-3.11/drivers/staging/bcm/CmHost.c:1294]: (error) Array 'psfCSType.cCPacketClassificationRule.u8IPv6FlowLable[3]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/staging/comedi/drivers/ni_mio_common.c:5044]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/staging/cxt1e1/comet.c:441]: (error) Array 'table[24][5]' index table[24][0] out of bounds. [linux-3.11/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c:2462]: (error) va_list 'args' was opened but not closed by va_end(). [linux-3.11/drivers/staging/ozwpan/ozpd.c:177]: (error) Possible null pointer dereference: pd [linux-3.11/drivers/staging/rtl8192u/ieee80211/aes.c:311]: (error) Array 'ctx.E[60]' accessed at index 60, which is out of bounds. [linux-3.11/drivers/staging/rtl8192u/ieee80211/aes.c:311]: (error) Array 'ctx.E[60]' accessed at index 61, which is out of bounds. [linux-3.11/drivers/staging/rtl8192u/ieee80211/aes.c:311]: (error) Array 'ctx.E[60]' accessed at index 62, which is out of bounds. [linux-3.11/drivers/staging/rtl8192u/ieee80211/aes.c:311]: (error) Array 'ctx.E[60]' accessed at index 63, which is out of bounds. [linux-3.11/drivers/staging/rtl8192u/r8192U_core.c:4723]: (error) Possible null pointer dereference: driver_info [linux-3.11/drivers/staging/rtl8712/rtl871x_ioctl_linux.c:1830]: (error) Memory leak: param [linux-3.11/drivers/staging/rtl8712/rtl871x_mlme.c:1296]: (error) Memory leak: pcmd [linux-3.11/drivers/staging/rtl8712/rtl871x_mlme.c:1296]: (error) Memory leak: psetkeyparm [linux-3.11/drivers/staging/tidspbridge/pmgr/dspapi.c:1787]: (error) Uninitialized variable: mask [linux-3.11/drivers/staging/tidspbridge/pmgr/dspapi.c:363]: (error) Uninitialized variable: num_nodes [linux-3.11/drivers/staging/tidspbridge/pmgr/dspapi.c:396]: (error) Uninitialized variable: num_procs [linux-3.11/drivers/staging/tidspbridge/pmgr/dspapi.c:503]: (error) Uninitialized variable: index [linux-3.11/drivers/staging/usbip/userspace/libsrc/names.c:179]: (error) Memory leak: p [linux-3.11/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c:130]: (error) Common realloc mistake: 'edev' nulled but not freed upon failure [linux-3.11/drivers/staging/vt6656/bssdb.c:596]: (error) Division by zero. [linux-3.11/drivers/staging/xgifb/vb_init.c:286]: (error) Array 'pVBInfo.CR40[3]' accessed at index 11, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:289]: (error) Array 'pVBInfo.CR40[3]' accessed at index 12, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:292]: (error) Array 'pVBInfo.CR40[3]' accessed at index 13, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:306]: (error) Array 'pVBInfo.CR40[3]' accessed at index 11, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:310]: (error) Array 'pVBInfo.CR40[3]' accessed at index 12, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:314]: (error) Array 'pVBInfo.CR40[3]' accessed at index 13, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:325]: (error) Array 'pVBInfo.CR40[3]' accessed at index 13, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:338]: (error) Array 'pVBInfo.CR40[3]' accessed at index 12, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:342]: (error) Array 'pVBInfo.CR40[3]' accessed at index 11, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:367]: (error) Array 'pVBInfo.CR40[3]' accessed at index 13, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:375]: (error) Array 'pVBInfo.CR40[3]' accessed at index 12, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:378]: (error) Array 'pVBInfo.CR40[3]' accessed at index 11, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:411]: (error) Array 'pVBInfo.CR40[3]' accessed at index 8, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:412]: (error) Array 'pVBInfo.CR40[3]' accessed at index 5, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:413]: (error) Array 'pVBInfo.CR40[3]' accessed at index 6, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:414]: (error) Array 'pVBInfo.CR40[3]' accessed at index 7, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:433]: (error) Array 'pVBInfo.CR40[3]' accessed at index 9, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:436]: (error) Array 'pVBInfo.CR40[3]' accessed at index 10, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:447]: (error) Array 'pVBInfo.CR40[3]' accessed at index 3, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:463]: (error) Array 'pVBInfo.CR40[3]' accessed at index 20, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:467]: (error) Array 'pVBInfo.CR40[3]' accessed at index 23, which is out of bounds. [linux-3.11/drivers/staging/xgifb/vb_init.c:478]: (error) Array 'pVBInfo.CR40[3]' accessed at index 4, which is out of bounds. [linux-3.11/drivers/tty/serial/msm_serial.c:818]: (error) Uninitialized variable: baud [linux-3.11/drivers/tty/serial/vt8500_serial.c:564]: (error) Uninitialized variable: port [linux-3.11/drivers/tty/sysrq.c:139]: (error) Possible null pointer dereference: killer [linux-3.11/drivers/usb/atm/cxacru.c:1111]: (error) Uninitialized variable: bp [linux-3.11/drivers/usb/atm/usbatm.c:1382]: (error) Undefined behavior: Variable 'buffer' is used as parameter and destination in s[n]printf(). [linux-3.11/drivers/usb/class/usbtmc.c:808]: (error) Uninitialized variable: actual [linux-3.11/drivers/usb/gadget/mv_u3d_core.c:315]: (error) Memory leak: trb [linux-3.11/drivers/usb/gadget/mv_u3d_core.c:459]: (error) Memory leak: trb [linux-3.11/drivers/usb/serial/mos7840.c:1373]: (error) Uninitialized variable: mos7840_port [linux-3.11/drivers/usb/serial/mos7840.c:1375]: (error) Uninitialized variable: mos7840_port [linux-3.11/drivers/usb/serial/mos7840.c:1395]: (error) Uninitialized variable: mos7840_port [linux-3.11/drivers/video/aty/mach64_ct.c:180]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/video/aty/mach64_ct.c:183]: (error) Shifting by a negative value is undefined behaviour [linux-3.11/drivers/video/broadsheetfb.c:673]: (error) Memory leak: sector_buffer [linux-3.11/drivers/video/cyber2000fb.c:1962]: (error) Uninitialized variable: err [linux-3.11/drivers/video/sis/init301.c:5717]: (error) Division by zero. [linux-3.11/fs/btrfs/reada.c:434] -> [linux-3.11/fs/btrfs/reada.c:450]: (warning) Possible null pointer dereference: fs_info - otherwise it is redundant to check it against null. [linux-3.11/fs/fuse/file.c:2058]: (error) Possible null pointer dereference: pages [linux-3.11/fs/jfs/jfs_txnmgr.c:1933]: (error) Uninitialized struct member: pxd.addr1 [linux-3.11/fs/jfs/jfs_txnmgr.c:1933]: (error) Uninitialized struct member: pxd.addr2 [linux-3.11/fs/jfs/jfs_txnmgr.c:1933]: (error) Uninitialized struct member: pxd.len [linux-3.11/fs/jfs/jfs_txnmgr.c:1933]: (error) Uninitialized variable: pxd [linux-3.11/fs/xfs/xfs_dir2_node.c:491]: (error) Uninitialized variable: lowstale [linux-3.11/fs/xfs/xfs_dir2_node.c:492]: (error) Uninitialized variable: highstale [linux-3.11/kernel/posix-timers.c:382]: (error) Uninitialized variable: __timr [linux-3.11/kernel/posix-timers.c:776]: (error) Uninitialized variable: __timr [linux-3.11/kernel/posix-timers.c:809]: (error) Uninitialized variable: __timr [linux-3.11/kernel/posix-timers.c:892]: (error) Uninitialized variable: __timr [linux-3.11/kernel/posix-timers.c:940]: (error) Uninitialized variable: __timr [linux-3.11/lib/argv_split.c:92]: (error) Memory leak: argv [linux-3.11/net/ipv6/ip6_gre.c:673]: (error) Uninitialized variable: max_headroom [linux-3.11/net/ipv6/netfilter/nf_conntrack_reasm.c:422]: (error) Uninitialized variable: head [linux-3.11/net/ipv6/reassembly.c:454]: (error) Uninitialized variable: head [linux-3.11/net/wireless/wext-sme.c:351]: (error) Possible null pointer dereference: ie [linux-3.11/scripts/docproc.c:394]: (error) Common realloc mistake: 'data' nulled but not freed upon failure [linux-3.11/scripts/docproc.c:418]: (error) Common realloc mistake: 'all_list' nulled but not freed upon failure [linux-3.11/scripts/dtc/checks.c:116]: (error) va_list 'ap' was opened but not closed by va_end(). [linux-3.11/scripts/dtc/fdtput.c:99]: (error) Common realloc mistake: 'value' nulled but not freed upon failure [linux-3.11/scripts/dtc/util.c:214]: (error) Common realloc mistake: 'buf' nulled but not freed upon failure [linux-3.11/scripts/dtc/util.h:34]: (error) va_list 'ap' was opened but not closed by va_end(). [linux-3.11/scripts/genksyms/genksyms.c:875]: (error) Resource leak: dumpfile [linux-3.11/scripts/kconfig/confdata.c:62]: (error) va_list 'ap' was opened but not closed by va_end(). [linux-3.11/scripts/kconfig/nconf.gui.c:375]: (error) Common realloc mistake: 'result' nulled but not freed upon failure [linux-3.11/scripts/kconfig/nconf.gui.c:481]: (error) Common realloc mistake: 'result' nulled but not freed upon failure [linux-3.11/scripts/kconfig/symbol.c:903]: (error) Common realloc mistake: 'res' nulled but not freed upon failure [linux-3.11/security/apparmor/match.c:64]: (error) Uninitialized struct member: th.td_data [linux-3.11/security/apparmor/match.c:64]: (error) Uninitialized struct member: th.td_hilen [linux-3.11/sound/oss/midi_synth.c:51]: (error) Uninitialized variable: len [linux-3.11/sound/oss/midi_synth.c:57]: (error) Uninitialized variable: len [linux-3.11/sound/oss/midi_synth.c:61]: (error) Uninitialized variable: len [linux-3.11/sound/oss/midi_synth.c:66]: (error) Uninitialized variable: len [linux-3.11/sound/oss/midi_synth.c:70]: (error) Uninitialized variable: len [linux-3.11/sound/oss/midi_synth.c:74]: (error) Uninitialized variable: len [linux-3.11/sound/oss/midi_synth.c:79]: (error) Uninitialized variable: len [linux-3.11/sound/oss/mpu401.c:1661]: (error) Uninitialized variable: len [linux-3.11/sound/oss/mpu401.c:1672]: (error) Uninitialized variable: len [linux-3.11/sound/oss/mpu401.c:1682]: (error) Uninitialized variable: len [linux-3.11/sound/oss/mpu401.c:1690]: (error) Uninitialized variable: len [linux-3.11/sound/pci/ice1712/wm8766.c:256]: (error) Uninitialized variable: val2 [linux-3.11/sound/pci/ice1712/wm8776.c:529]: (error) Uninitialized variable: val2 [linux-3.11/sound/soc/codecs/ad193x.c:488]: (error) Uninitialized variable: ret [linux-3.11/sound/soc/codecs/tlv320aic23.c:675]: (error) Uninitialized variable: ret [linux-3.11/sound/soc/nuc900/nuc900-ac97.c:337]: (error) Uninitialized variable: ret [linux-3.11/sound/soc/txx9/txx9aclc-ac97.c:190]: (error) Uninitialized variable: drvdata [linux-3.11/sound/soc/txx9/txx9aclc-ac97.c:192]: (error) Uninitialized variable: drvdata [linux-3.11/tools/lib/traceevent/event-parse.c:250]: (error) Common realloc mistake: 'cmdlines' nulled but not freed upon failure [linux-3.11/tools/perf/bench/sched-messaging.c:335]: (error) Memory leak: pth_tab [linux-3.11/tools/perf/perf.c:273]: (error) Common realloc mistake: 'new_argv' nulled but not freed upon failure [linux-3.11/tools/perf/tests/dso-data.c:47]: (error) Memory leak: buf [linux-3.11/tools/perf/util/annotate.c:680]: (error) Memory leak: ppercents [linux-3.11/tools/power/cpupower/bench/parse.c:166]: (error) Resource leak: configfile [linux-3.11/tools/power/cpupower/bench/parse.c:87]: (error) Common realloc mistake: 'filename' nulled but not freed upon failure [linux-3.11/tools/power/cpupower/utils/cpupower.c:96]: (error) Memory leak: page [linux-3.11/tools/testing/selftests/timers/posix_timers.c:155]: (error) Uninitialized variable: err cppcheck-1.82/triage/readme.txt000066400000000000000000000005751322667425100165140ustar00rootroot00000000000000This folder is for triage data of cppcheck results. You can scan these projects with arbitrary cppcheck version and get triaged reports. Usage: 1. Pick a project, for example linux-3.11. 2. Scan linux-3.11 on your computer with cppcheck (arbitrary version). 3. run the triage.py script: python triage.py linux-3.11 path-to-cppcheck-results.txt 4. A report.html is generated cppcheck-1.82/triage/theme1.css000066400000000000000000000007041322667425100164050ustar00rootroot00000000000000th { background-color: #555; border: 1px solid #555; color: white; font-size: 80%; padding: 3px; text-align: left; vertical-align: top; } td { border: 1px solid #d4d4d4; font-size: 80%; padding: 7px 5px; vertical-align: top; } .code { background-color: lightgray; border: 1px solid gray; font-family: courier; } .tp { color: red; } .fp { color: green; } .notfound { color: blue; } .untriaged { color: black; } cppcheck-1.82/triage/triage-report.py000066400000000000000000000065601322667425100176540ustar00rootroot00000000000000#!/usr/bin/env python import sys import re if len(sys.argv) != 3: print('usage: triage.py project resultsfile.txt') sys.exit(1) project = sys.argv[1] resultfile = sys.argv[2] f = open(project + '/true-positives.txt', 'rt') truepositives = f.read() f.close() f = open(project + '/false-positives.txt', 'rt') falsepositives = f.read() f.close() fin = open(resultfile, 'rt') results = fin.read() fin.close() out = { 'untriaged': '', 'fn': '', 'fp': '', 'tp': '' } numberOfFalsePositives = 0 numberOfTruePositives = 0 numberOfFalseNegatives = 0 for result in results.split('\n'): result = result.strip() res = re.match('\\[(' + project + '.+):([0-9]+)\\]:\s+[(][a-z]+[)] (.+)', result) if res is None: continue filename = res.group(1) linenr = res.group(2) message = res.group(3) css = 'untriaged' classification = 'Untriaged' if result in truepositives: css = 'tp' classification = 'True Positive' numberOfTruePositives += 1 elif result in falsepositives: css = 'fp' classification = 'False Positive' numberOfFalsePositives += 1 href = None html = ' ' html += '' + filename + '' html += '' + linenr + '' html += '' + message + '' if project == 'linux-3.11': href = 'http://github.com/torvalds/linux/blob/v3.11' + filename[filename.find('/'):] + '#L' + linenr if href: html += '' + classification + '' else: html += '' + classification + '' html += '\n' out[css] += html f = open(project + '/true-positives.txt', 'rt') for line in f.readlines(): line = line.strip() if '] -> [' in line or '(error)' not in line: continue res = re.match('\\[(' + project + '.+):([0-9]+)\\]:\s+[(][a-z]+[)] (.+)', line) if res is None: continue if line in results: continue numberOfFalseNegatives += 1 filename = res.group(1) linenr = res.group(2) message = res.group(3) classification = 'False Negative' css = 'fn' html = ' ' html += '' + filename + '' html += '' + linenr + '' html += '' + message + '' html += '' + classification + '' html += '\n' out[css] += html f.close() project2 = '' if '-' in project: project2 = project[:project.find('-')] else: project2 = project fout = open('report.html', 'wt') fout.write('Cppcheck results for ' + project + '\n') fout.write('

Cppcheck results for ' + project + '

\n') fout.write('

Number of false negatives: ' + str(numberOfFalseNegatives) + '

\n') fout.write('

Number of true positives: ' + str(numberOfTruePositives) + '

\n') fout.write('

Number of false positives: ' + str(numberOfFalsePositives) + '

\n') fout.write('\n') fout.write('\n') fout.write(out['fn']) fout.write(out['tp']) fout.write(out['fp']) fout.write(out['untriaged']) fout.write('
FilenameLineMessageClassification
') fout.close() cppcheck-1.82/webreport.sh000077500000000000000000000007201322667425100156030ustar00rootroot00000000000000#!/bin/bash ./generate_coverage_report rm -rf devinfo mkdir devinfo mv coverage_report devinfo/ doxygen 2> devinfo/doxygen-errors.txt mv doxyoutput/html devinfo/doxyoutput cd addons doxygen cppcheckdata.doxyfile mv html ../devinfo/cppcheckdata cd .. # Detect duplicate code.. ~/pmd-4.2.6/bin/cpd.sh lib/ > devinfo/cpd.txt #java -jar ~/simian-2.4.0/bin/simian-2.4.0.jar -language=c++ -reportDuplicateText -threshold=10 lib/*.cpp lib/*.h > devinfo/simian.txt cppcheck-1.82/win_installer/000077500000000000000000000000001322667425100161065ustar00rootroot00000000000000cppcheck-1.82/win_installer/GPLv3.txt000066400000000000000000001004171322667425100175450ustar00rootroot00000000000000 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.cppcheck-1.82/win_installer/config.wxi000066400000000000000000000013351322667425100201060ustar00rootroot00000000000000 cppcheck-1.82/win_installer/cppcheck.sln000066400000000000000000000015431322667425100204070ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "cppcheck", "cppcheck.wixproj", "{3B772885-4980-4A76-8407-4DABF8F7757C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3B772885-4980-4A76-8407-4DABF8F7757C}.Release|x64.ActiveCfg = Release|x64 {3B772885-4980-4A76-8407-4DABF8F7757C}.Release|x64.Build.0 = Release|x64 {3B772885-4980-4A76-8407-4DABF8F7757C}.Release|x86.ActiveCfg = Release|x86 {3B772885-4980-4A76-8407-4DABF8F7757C}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = TRUE EndGlobalSection EndGlobal cppcheck-1.82/win_installer/cppcheck.wixproj000066400000000000000000000035161322667425100213170ustar00rootroot00000000000000 false $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets Release x86 cppcheck-$(ProductVersion)-$(Platform)-Setup Build\ BuildTmp\Wix\$(Platform)\ package {3b772885-4980-4a76-8407-4dabf8f7757c} WixUIExtension cppcheck-1.82/win_installer/cppcheck.wxs000066400000000000000000000351351322667425100204400ustar00rootroot00000000000000 NOT Installed NOT Installed NOT Installed NOT Installed NOT Installed 1 NOT Installed Installed AND PATCH Installed NOT Installed 1 NOT Installed OR WixUI_InstallMode = "Change" Installed AND NOT PATCH Installed AND PATCH 1 1 1 1 1 cppcheck-1.82/win_installer/images/000077500000000000000000000000001322667425100173535ustar00rootroot00000000000000cppcheck-1.82/win_installer/images/banner.jpg000066400000000000000000000047451322667425100213340ustar00rootroot00000000000000JFIF__C     C   : ڐX j}߉g e3!,[bQxE|D}θ1 vS6gQ, [;OD|2OJigV9.@P2`*R2ݳ+Ge _,`j MQdp_s>ʨ҅]bXTG 3E27g[3EԶ"4 Oh&MŌhżkQeY2E?1P!`p?fD=nY Rx%7]c 2M/ "Q!1PAaq#4`p?ْ UhUZR%o݅H %X9o9'qlQ_öSDi (g)9>x}9@&42ͻƞMYaH g9o}k}2:ٸ0ǀD[ _yf TaQ,rc3Y@ *4LI$ǘP\ WUk 2? ^yƩSaY>P(b 6 !ΌT@ f;zܸ( (vmS9ǭˀG`#`6TqrX f;zܸ(fF p(vmS9ǭˀ&2s=Pڧws[O0ˈ f;zܸ,(| (vmS9ǭˀdI f;zܸ2)`6Tqr}lٵN. !>m;6 yfVb 6TqrFm;6 O1CPڧws[m;6eͪwx8pCj*056P32 4@$wlwb%>6I)݊"EDLI ,%vBEhA~oFє ~];Y IBSc y:WP$Fv(<}M MP-y6ʙϕ3CBScW`U9jpdIP hJlwbhnq5!ДS g50Zv)(AjD)݊Gx2Д'vʧBΑ9xXi%6;N:M{^uR"Nv(<}M MP-y6Ê!tLNkXj!t%6;HEyE;X+FSc C''Ei)݊O#xBScu/No:D !axqo!.~v~+59%6;@>qsZhJlwbh!Д!IěLυ37Jlw`gKtf+BScSW.hJlwa *׀M M!O\4Ci)݂j{'}d#ДScMe6;3Pq@?N3v8';|goqN3v8';|goqN3v8';|goqN3v8';|goqN3v8';|goW3Pq@?zAH2_= |/e zAH2_= |/e zAH2_= |/e zAH2_= |/e zAH2_= |/eW5 !0Pqt12A3Q"ar #$R4@?(4[I&$谂)9/H7һFh䌻[>>cj|٫% U3ei$ "ˣIf2l ő# HW9*pSD*k4'Q-yƥIg < Tn83E/&0mle]{2l üT&_V8YS~f`^JjTJ<REIX7%\le]j\a5kc(?. ~ =[Fh$~^2N$2%n2l LITyD5K4[k_\c"2l ɮcg(YRiPI_PlG 9XV=dZG*Q-y!R GTU4[kXvQ-y5X,`qkc(9 #~߼xp$3%n2l \"U?yG!SizX3E/&vQ-y5X,`qkc(BpQkW{+Bxy6?Q-y+TJ08Gac('4[kXvQ-y4$R83qrY3E/4q ZTz=`WPQ-xDݵf`^/~Q(0mleߔJ1>=[Fh`M_H\iV9vòW+Fh(4[(!1PAaQpqс?!Æ=$+vSЎݔQ8\Lp3c,pehZԏiŝ_}t@ Ca*u6"F(ӃT4z~~{ݴoEMmb *'rH[qMm}ǭ{6SU$Hme[ k[ql)>.c|L :یyH5{[qzBa:@e$5!HT27D1]O2>uhhPV.fwdkn<^kٽK\o*.V} mjvƢ:ۏnSh=mNj7)MPbE,Ȃn n03]Oʶk m ߼qn<^Mo0#.Vw3)D UYp1S'FNk[qÚ Y߼qn<^Mo.拕@-5ݐJuZ$)JaN_#ժ^=|N6&koOl7zӍp=iPAKR}3i!?6p mma$I$I$I$I$I6mmI$I$I$I$I$MmmI$I$I$I$I$memI$I$I$I$I$mI-a$I$I$I$I$I6mKmI$I$I$I$I$M$mI$I$I$I$I$m-mI$I$I$I$I$mYma$I$I$I$I$I6m[mI$I$I$I$I$MlmI$I$I$I$I$mdmI$I$I$I$I$mma$I$I$I$I$I6mmI$I$I$I$I$MemI$I$I$I$I$m$mI$I$I$I$I$mY-a$I$I$I$I$I6mmI$I$I$I$I$M%mI$I$I$I$I$memI$I$I$I$I$mYma$I$I$I$I$I6m[mI$I$I$I$I$MemI$I$I$I$I$m%mI$I$I$I$I$mY-a$I$I$I$I$I6m[mI$I$I$I$I$MemI$I$I$I$I$m$mI$I$I$I$I$mI-a$I$I$I$I$I6m[mI$I$I$I$I$MdmI$I$I$I$I$mmmI$I$I$I$I$mma$I$I$I$I$I6l[mI$I$I$I$I$M-mI$I$I$I$I$mmmI$I$I$I$I$mma$I$I$I$I$I6mmI$I$I$I$I$MmmI$I$I$I$I$Pa@?>>>>>>>>>>>>>>>>>>>P@?䉥M/i}K_D&4M/i}K_D&4M/i}K_D&4M/i}K_D&4M/i}K_D&_(!Q1APqap?B-'n c2m@cBʚ_袉b.-yT{?n-:ILb# NkatE_RT@ͻGae&]!j#!+dk *"%7&dHfXD6# hև^c<_%yο%1(S3LL.P #GoeK| ,KQXmdhJuy~vƎ b)ȯj!o~(aVj@j7P'Dtd$6}&ha>b;7uS5#e-5BJߺH2 &.yPjDR3 m;ZK-y~v cppcheck-1.82/win_installer/readme.txt000066400000000000000000000025011322667425100201020ustar00rootroot00000000000000The Wix Installer for Windows ============================= cppcheck Windows installer is created with WiX: http://wixtoolset.org/ You'll need: - WiX - MSBuild (coming with Visual Studio) - VS 2015 CRT merge module Configuring ----------- Installer configuration is done in file config.wxi. Depending how you build cppcheck you may need to alter the paths for binaries. Product version and other info ------------------------------ Version number and product name are set in file productInfo.wxi. Building installer ------------------ Before building the installer make sure all the components are build: - LIB dynamic link library (cppcheck-core.dll) - CLI executable (cppcheck.exe) - GUI executable (cppcheck-gui.exe) - GUI translations (*.qm) - generated by the Qt's lrelease tool And that runtime files are available: - Qt runtimes: Qt5Core.dll, Qt5PrintSupport.dll, Qt5Widgets.dll, Qt5Gui.dll and platforms/qwindows.dll - MS CRT merge module (Microsoft_VC140_CRT_x86.msm or Microsoft_VC140_CRT_x64.msm) Build installer by giving this command line in VS command prompt (or run vcvars32.bat in DOS prompt first to setup environment): > msbuild cppcheck.wixproj /p:Platform=x86,ProductVersion=X.YY For example: > msbuild cppcheck.wixproj /p:Platform=x86,ProductVersion=1.40 Installer is created to Build -folder.