pax_global_header00006660000000000000000000000064123671377300014523gustar00rootroot0000000000000052 comment=80eef66d6d4a8d1b7537d0f7c293242dfa73a25b cppcheck-1.66/000077500000000000000000000000001236713773000132375ustar00rootroot00000000000000cppcheck-1.66/.gitignore000066400000000000000000000014111236713773000152240ustar00rootroot00000000000000*.bak *.gcno *.o *.pyc cppcheck cppcheck.exe dmake 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/gui.sln gui/gui.vcproj gui/Makefile gui/Makefile.debug gui/Makefile.release gui/qrc_gui.cpp # Doxygen output folder doxyoutput/ # qmake generated htmlreport/.tox/ htmlreport/MANIFEST # Backup files and stuff from patches *.orig *.rej *~ # kdevelop 4.x *.kdev4 cppcheck-1.66/.mailmap000066400000000000000000000054471236713773000146720ustar00rootroot00000000000000Andreas 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.66/.travis.yml000066400000000000000000000075751236713773000153660ustar00rootroot00000000000000language: cpp compiler: - clang - gcc env: global: # unfortunately we need this to stay within 50min timelimit given by travis. # this also turns off the debug/warning cxxflags - CXXFLAGS="-O2 -march=native -mtune=native -Wunreachable-code" 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" - SRCDIR=build CHECK_CLANG=yes VERIFY=1 - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" MAKEFLAGS="HAVE_RULES=yes" SRCDIR=build VERIFY=1 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: clang env: SRCDIR=build CHECK_CLANG=yes VERIFY=1 before_install: # install needed deps - sudo apt-get update -qq - sudo apt-get install -qq python-pygments libqt4-core libqt4-gui libqt4-dev qt4-dev-tools qt4-qmake libxml2-utils libpcre3 gdb unzip script: # 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" == "gcc" ]]; then wget "https://github.com/llvm-mirror/clang/archive/0d9772d6c4fd0f69f7bdbb692768f07f9af2a3d0.zip" & make -j 4 & wait; unzip 0d9772d6c4fd0f69f7bdbb692768f07f9af2a3d0.zip > /dev/null; touch /tmp/clang.cppcheck; cd ./clang-0d9772d6c4fd0f69f7bdbb692768f07f9af2a3d0 ; ../cppcheck . --max-configs=1 --enable=all --inconclusive --exception-handling -iINPUTS -itest/CXX/temp/temp.decls/temp.mem/p5.cpp -itest/CodeGenCXX/mangle-alias-template.cpp -itest/Lexer/unicode.c -j 2 |& tee /tmp/clang.cppcheck; cd ../ ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/clang.cppcheck; exit; fi # compile cppcheck, default build - make -j4 - make test -j4 # compile gui - cd gui - qmake - make -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 -Ilib --enable=style,performance,portability,warning,internal --exception-handling --suppressions-list=.travis_suppressions . -j 2 |& tee /tmp/cppcheck.cppcheck - sh -c "! grep '^\[' /tmp/cppcheck.cppcheck" - 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 - make -j4 - cd ../ # check htmlreport stuff - ./htmlreport/test_htmlreport.py - cd htmlreport - ./check.sh - cd ../ # check if DESTDIR works TODO: actually execute this - mkdir install_test - make DESTDIR=install_test install # rm everything - git clean -dfx # check what happens if we want to install it to some other dir, - make 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 - make 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 if Makefile needs to be regenerated - git clean -dfx - make dmake # now, if dmake modified the makefile, return false! - git diff --exit-code notifications: irc: channels: - "irc.freenode.org#cppcheck" template: - "[%{commit} : %{author}] %{message}" - "%{build_url}" skip_join: true cppcheck-1.66/.travis_suppressions000066400000000000000000000003721236713773000174070ustar00rootroot00000000000000unusedPrivateFunction:test/testcmdlineparser.cpp redundantNextPrevious:test/testtoken.cpp uselessAssignmentPtrArg:build/checkstl.cpp *:gui/test* *:test/test.cxx *:democlient* *:externals* *:htdocs* *:htmlreport* *:samples* *:tools* *:win_installer* cppcheck-1.66/AUTHORS000066400000000000000000000041751236713773000143160ustar00rootroot00000000000000The cppcheck team, in alphabetical order: Abhishek Bharadwaj Ahti Legonkov Akio Idehara Aleksey Palazhchenko Alexander Mai Alexey Zhikhartsev Andreas Bießmann Andrei Karas Andrew C. Martin Andy Maloney Ankita Gupta anuraggarg011 Armin Müller Arpit Chaudhary August Sodora Baris Demiray Benjamin Goose Benjamin Kramer Benjamin Wolsey Bill Egert booga Carlo Marcelo Arenas Belon Cary R Changkyoon Kim Chuck Larson Daniel Marjamäki Debrard Sebastien Deepak Gupta dencat Dirk Jagdmann Dmitry-Me Duraffort Edoardo Prezioso Elbert Pol Emmanuel Blot Eric Sesterhenn Erik Lax Ettl Martin Felipe Pena Felix Geyer Frank Zingsheim gaurav kaushik Gerhard Zlabinger Gianluca Scacco Goran Džaferi Graham Whitted Greg Hewgill Guillaume Miossec Günther Makulik Heinrich Schuchardt Henrik Nilsson He Yuqi Hoang Tuan Su Jay Sigbrandt Jens Bäckman János Maros Johan Samuelson John Smits Jonathan Neuschäfer Jose Roquette Joshua Beck Julian Santander Jussi Lehtola Kamil Dudka Kartik Bajaj Kevin Christian Kimmo Varis Konrad Windszus Kumar Ashwani larudwer Lauri Nurmi Leandro Lisboa Penz Lena Herscheid Lieven de Cock lioncash Lucas Manuel Rodriguez Marc-Antoine Perennou Marek Zmysłowski Mark de Wever Markus Elfring Martin Ettl Martin Exner Massimo Paladin Mateusz Pusz Mathias De Maré Matthias Krüger Mika Attila Mohit Mate Monika Lukow Moritz Barsnick Moritz Lipp Moshe Kaplan ms Nguyen Duong Tuan Nicolás Alvarez Nicolas Le Cam Nilesh Kumar OGAWA KenIchi Oliver Stoeneberg Pavel Roschin Pete Johns Peter Pentchev Philipp Kloke Pierre Schweitzer Pino Toscano Pranav Khanna Raphael Geissert Reijo Tomperi Richard Quirk Robert Morin Robert Reif rofl0r Roman Zaytsev Borisovich root Ryan Pavlik Sam Truscott Sandeep Dutta Sébastien Debrard Simon Kagstrom Simon Martin Slava Semushin Stefan Beller Stefan Naewe Stefan Weil Steve Duan Steven Myint Thomas Arnhold Thomas Jarosch Thomas Sondergaard Tim Gerundt Tobias Weibel Toralf Förster Troshin V.S. Valerii Lashmanov WenChung Chiu Vesa Pikki Ville Skyttä XhmikosR Xuecheng Zhang Zachary Blair Zhao Qifa Zhiyuan Zhang Дмитрий Старцев GUI graphics courtesy of Tango Desktop Project: http://tango.freedesktop.org cppcheck-1.66/COPYING000066400000000000000000001045131236713773000142760ustar00rootroot00000000000000 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.66/Cppcheck.xcodeproj/000077500000000000000000000000001236713773000167535ustar00rootroot00000000000000cppcheck-1.66/Cppcheck.xcodeproj/project.pbxproj000066400000000000000000002327421236713773000220410ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 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 */; }; 39E60EBE1270DE3A00AC0D02 /* checkobsoletefunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E971270DE3A00AC0D02 /* checkobsoletefunctions.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 */; }; 39E60EC51270DE3A00AC0D02 /* executionpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EA51270DE3A00AC0D02 /* executionpath.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 */; }; F4043DD9177F093300CD5A40 /* checkassignif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB7177F093300CD5A40 /* checkassignif.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 */; }; F4043DDF177F093300CD5A40 /* checknonreentrantfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC3177F093300CD5A40 /* checknonreentrantfunctions.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 */; }; 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 /* testobsoletefunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA117AB8511006C06AF /* testobsoletefunctions.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 */; }; F4C3489D18256A4500521683 /* checkassignif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB7177F093300CD5A40 /* checkassignif.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 */; }; F4C348A818256A4500521683 /* checknonreentrantfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC3177F093300CD5A40 /* checknonreentrantfunctions.cpp */; }; F4C348A918256A4500521683 /* checknullpointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC5177F093300CD5A40 /* checknullpointer.cpp */; }; F4C348AA18256A4500521683 /* checkobsoletefunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E971270DE3A00AC0D02 /* checkobsoletefunctions.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 */; }; F4C348B318256A4500521683 /* executionpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EA51270DE3A00AC0D02 /* executionpath.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 */ 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 = ""; }; 39E60E971270DE3A00AC0D02 /* checkobsoletefunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkobsoletefunctions.cpp; path = lib/checkobsoletefunctions.cpp; sourceTree = ""; }; 39E60E981270DE3A00AC0D02 /* checkobsoletefunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkobsoletefunctions.h; path = lib/checkobsoletefunctions.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 = ""; }; 39E60EA51270DE3A00AC0D02 /* executionpath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = executionpath.cpp; path = lib/executionpath.cpp; sourceTree = ""; }; 39E60EA61270DE3A00AC0D02 /* executionpath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = executionpath.h; path = lib/executionpath.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 = ""; }; F4043DB7177F093300CD5A40 /* checkassignif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkassignif.cpp; path = lib/checkassignif.cpp; sourceTree = ""; }; F4043DB8177F093300CD5A40 /* checkassignif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkassignif.h; path = lib/checkassignif.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 = ""; }; F4043DC3177F093300CD5A40 /* checknonreentrantfunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checknonreentrantfunctions.cpp; path = lib/checknonreentrantfunctions.cpp; sourceTree = ""; }; F4043DC4177F093300CD5A40 /* checknonreentrantfunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checknonreentrantfunctions.h; path = lib/checknonreentrantfunctions.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 /* testobsoletefunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testobsoletefunctions.cpp; path = test/testobsoletefunctions.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 = ""; }; 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 */, 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 = ( 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 */, F4043DB7177F093300CD5A40 /* checkassignif.cpp */, F4043DB8177F093300CD5A40 /* checkassignif.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 */, F4043DC3177F093300CD5A40 /* checknonreentrantfunctions.cpp */, F4043DC4177F093300CD5A40 /* checknonreentrantfunctions.h */, F4043DC5177F093300CD5A40 /* checknullpointer.cpp */, F4043DC6177F093300CD5A40 /* checknullpointer.h */, 39E60E971270DE3A00AC0D02 /* checkobsoletefunctions.cpp */, 39E60E981270DE3A00AC0D02 /* checkobsoletefunctions.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 */, 39E60EA51270DE3A00AC0D02 /* executionpath.cpp */, 39E60EA61270DE3A00AC0D02 /* executionpath.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 /* testobsoletefunctions.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 = 0460; }; 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 */, 39E60EBD1270DE3A00AC0D02 /* checkmemoryleak.cpp in Sources */, 39E60EBE1270DE3A00AC0D02 /* checkobsoletefunctions.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 */, 39E60EC51270DE3A00AC0D02 /* executionpath.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 */, F4043DD9177F093300CD5A40 /* checkassignif.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 */, F4043DDF177F093300CD5A40 /* checknonreentrantfunctions.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 */, 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 */, F4C3489D18256A4500521683 /* checkassignif.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 */, F4C348A818256A4500521683 /* checknonreentrantfunctions.cpp in Sources */, F4C348A918256A4500521683 /* checknullpointer.cpp in Sources */, F4C348AA18256A4500521683 /* checkobsoletefunctions.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 */, F4C348B318256A4500521683 /* executionpath.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 /* testobsoletefunctions.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 = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = 4.0; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 1DEB923708733DC60010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 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.66/Makefile000066400000000000000000001401021236713773000146750ustar00rootroot00000000000000# 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) 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 CXXFLAGS CXXFLAGS=-O2 -include lib/cxx11emu.h -DNDEBUG -Wall endif ifeq ($(HAVE_RULES),yes) 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 CXX CXX=g++ endif ifndef PREFIX PREFIX=/usr endif ifndef INCLUDE_FOR_LIB INCLUDE_FOR_LIB=-Ilib -Iexternals/tinyxml endif ifndef INCLUDE_FOR_CLI INCLUDE_FOR_CLI=-Ilib -Iexternals/tinyxml endif ifndef INCLUDE_FOR_TEST INCLUDE_FOR_TEST=-Ilib -Icli -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)/check.o \ $(SRCDIR)/check64bit.o \ $(SRCDIR)/checkassert.o \ $(SRCDIR)/checkassignif.o \ $(SRCDIR)/checkautovariables.o \ $(SRCDIR)/checkbool.o \ $(SRCDIR)/checkboost.o \ $(SRCDIR)/checkbufferoverrun.o \ $(SRCDIR)/checkclass.o \ $(SRCDIR)/checkexceptionsafety.o \ $(SRCDIR)/checkinternal.o \ $(SRCDIR)/checkio.o \ $(SRCDIR)/checkleakautovar.o \ $(SRCDIR)/checkmemoryleak.o \ $(SRCDIR)/checknonreentrantfunctions.o \ $(SRCDIR)/checknullpointer.o \ $(SRCDIR)/checkobsoletefunctions.o \ $(SRCDIR)/checkother.o \ $(SRCDIR)/checkpostfixoperator.o \ $(SRCDIR)/checksizeof.o \ $(SRCDIR)/checkstl.o \ $(SRCDIR)/checkuninitvar.o \ $(SRCDIR)/checkunusedfunctions.o \ $(SRCDIR)/checkunusedvar.o \ $(SRCDIR)/cppcheck.o \ $(SRCDIR)/errorlogger.o \ $(SRCDIR)/executionpath.o \ $(SRCDIR)/library.o \ $(SRCDIR)/mathlib.o \ $(SRCDIR)/path.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 CLIOBJ = cli/cmdlineparser.o \ cli/cppcheckexecutor.o \ cli/filelister.o \ cli/main.o \ cli/pathmatch.o \ cli/threadexecutor.o TESTOBJ = test/options.o \ test/test64bit.o \ test/testassert.o \ test/testassignif.o \ test/testautovariables.o \ test/testbool.o \ test/testboost.o \ test/testbufferoverrun.o \ test/testcharvar.o \ test/testclass.o \ test/testcmdlineparser.o \ test/testconstructors.o \ test/testcppcheck.o \ test/testdivision.o \ test/testerrorlogger.o \ test/testexceptionsafety.o \ test/testfilelister.o \ test/testincompletestatement.o \ test/testinternal.o \ test/testio.o \ test/testleakautovar.o \ test/testlibrary.o \ test/testmathlib.o \ test/testmemleak.o \ test/testnonreentrantfunctions.o \ test/testnullpointer.o \ test/testobsoletefunctions.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/testsimplifytokens.o \ test/testsizeof.o \ test/teststl.o \ test/testsuite.o \ test/testsuppressions.o \ test/testsymboldatabase.o \ test/testthreadexecutor.o \ test/testtimer.o \ test/testtoken.o \ test/testtokenize.o \ test/testuninitvar.o \ test/testunusedfunctions.o \ test/testunusedprivfunc.o \ test/testunusedvar.o \ test/testvalueflow.o ifndef TINYXML TINYXML = externals/tinyxml/tinyxml2.o endif EXTOBJ += $(TINYXML) .PHONY: dmake ###### Targets cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++0x -o cppcheck $(CLIOBJ) $(LIBOBJ) $(EXTOBJ) $(LIBS) $(LDFLAGS) $(RDYNAMIC) all: cppcheck testrunner testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o cli/pathmatch.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++0x -o testrunner $(TESTOBJ) $(LIBOBJ) cli/threadexecutor.o cli/cppcheckexecutor.o cli/cmdlineparser.o cli/filelister.o cli/pathmatch.o $(EXTOBJ) $(LIBS) $(LDFLAGS) $(RDYNAMIC) test: all ./testrunner check: all ./testrunner -g -q dmake: tools/dmake.o cli/filelister.o lib/path.o $(CXX) $(CXXFLAGS) -std=c++0x -o dmake tools/dmake.o cli/filelister.o lib/path.o -Ilib $(LDFLAGS) ./dmake reduce: tools/reduce.o externals/tinyxml/tinyxml2.o $(LIBOBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++0x -g -o reduce tools/reduce.o -Ilib -Iexternals/tinyxml $(LIBOBJ) $(LIBS) externals/tinyxml/tinyxml2.o $(LDFLAGS) $(RDYNAMIC) clean: rm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/tinyxml/*.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 . install: cppcheck install -d ${BIN} install cppcheck ${BIN} install htmlreport/cppcheck-htmlreport ${BIN} ifdef CFGDIR install -d ${CFGDIR} install -m 644 cfg/* ${CFGDIR} endif ###### Build $(SRCDIR)/check.o: lib/check.cpp lib/cxx11emu.h lib/check.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/check.o $(SRCDIR)/check.cpp $(SRCDIR)/check64bit.o: lib/check64bit.cpp lib/cxx11emu.h lib/check64bit.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/check64bit.o $(SRCDIR)/check64bit.cpp $(SRCDIR)/checkassert.o: lib/checkassert.cpp lib/cxx11emu.h lib/checkassert.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkassert.o $(SRCDIR)/checkassert.cpp $(SRCDIR)/checkassignif.o: lib/checkassignif.cpp lib/cxx11emu.h lib/checkassignif.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkassignif.o $(SRCDIR)/checkassignif.cpp $(SRCDIR)/checkautovariables.o: lib/checkautovariables.cpp lib/cxx11emu.h lib/checkautovariables.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkautovariables.o $(SRCDIR)/checkautovariables.cpp $(SRCDIR)/checkbool.o: lib/checkbool.cpp lib/cxx11emu.h lib/checkbool.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkbool.o $(SRCDIR)/checkbool.cpp $(SRCDIR)/checkboost.o: lib/checkboost.cpp lib/cxx11emu.h lib/checkboost.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkboost.o $(SRCDIR)/checkboost.cpp $(SRCDIR)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp lib/cxx11emu.h lib/checkbufferoverrun.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkbufferoverrun.o $(SRCDIR)/checkbufferoverrun.cpp $(SRCDIR)/checkclass.o: lib/checkclass.cpp lib/cxx11emu.h lib/checkclass.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkclass.o $(SRCDIR)/checkclass.cpp $(SRCDIR)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/cxx11emu.h lib/checkexceptionsafety.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkexceptionsafety.o $(SRCDIR)/checkexceptionsafety.cpp $(SRCDIR)/checkinternal.o: lib/checkinternal.cpp lib/cxx11emu.h lib/checkinternal.h lib/check.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkio.o $(SRCDIR)/checkio.cpp $(SRCDIR)/checkleakautovar.o: lib/checkleakautovar.cpp lib/cxx11emu.h lib/checkleakautovar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/checkmemoryleak.h lib/checkother.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkleakautovar.o $(SRCDIR)/checkleakautovar.cpp $(SRCDIR)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/cxx11emu.h lib/checkmemoryleak.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/checkuninitvar.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkmemoryleak.o $(SRCDIR)/checkmemoryleak.cpp $(SRCDIR)/checknonreentrantfunctions.o: lib/checknonreentrantfunctions.cpp lib/cxx11emu.h lib/checknonreentrantfunctions.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checknonreentrantfunctions.o $(SRCDIR)/checknonreentrantfunctions.cpp $(SRCDIR)/checknullpointer.o: lib/checknullpointer.cpp lib/cxx11emu.h lib/checknullpointer.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checknullpointer.o $(SRCDIR)/checknullpointer.cpp $(SRCDIR)/checkobsoletefunctions.o: lib/checkobsoletefunctions.cpp lib/cxx11emu.h lib/checkobsoletefunctions.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkobsoletefunctions.o $(SRCDIR)/checkobsoletefunctions.cpp $(SRCDIR)/checkother.o: lib/checkother.cpp lib/cxx11emu.h lib/checkother.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkother.o $(SRCDIR)/checkother.cpp $(SRCDIR)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/cxx11emu.h lib/checkpostfixoperator.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkpostfixoperator.o $(SRCDIR)/checkpostfixoperator.cpp $(SRCDIR)/checksizeof.o: lib/checksizeof.cpp lib/cxx11emu.h lib/checksizeof.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checksizeof.o $(SRCDIR)/checksizeof.cpp $(SRCDIR)/checkstl.o: lib/checkstl.cpp lib/cxx11emu.h lib/checkstl.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/executionpath.h lib/symboldatabase.h lib/checknullpointer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkstl.o $(SRCDIR)/checkstl.cpp $(SRCDIR)/checkuninitvar.o: lib/checkuninitvar.cpp lib/cxx11emu.h lib/checkuninitvar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/executionpath.h lib/checknullpointer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkuninitvar.o $(SRCDIR)/checkuninitvar.cpp $(SRCDIR)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp lib/cxx11emu.h lib/checkunusedfunctions.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkunusedfunctions.o $(SRCDIR)/checkunusedfunctions.cpp $(SRCDIR)/checkunusedvar.o: lib/checkunusedvar.cpp lib/cxx11emu.h lib/checkunusedvar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkunusedvar.o $(SRCDIR)/checkunusedvar.cpp $(SRCDIR)/cppcheck.o: lib/cppcheck.cpp lib/cxx11emu.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h lib/preprocessor.h lib/tokenize.h lib/tokenlist.h lib/checkunusedfunctions.h lib/check.h lib/version.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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/path.h lib/cppcheck.h lib/settings.h lib/library.h lib/mathlib.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/errorlogger.o $(SRCDIR)/errorlogger.cpp $(SRCDIR)/executionpath.o: lib/executionpath.cpp lib/cxx11emu.h lib/executionpath.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/executionpath.o $(SRCDIR)/executionpath.cpp $(SRCDIR)/library.o: lib/library.cpp lib/cxx11emu.h lib/library.h lib/config.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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 $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/mathlib.o $(SRCDIR)/mathlib.cpp $(SRCDIR)/path.o: lib/path.cpp lib/cxx11emu.h lib/path.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/path.o $(SRCDIR)/path.cpp $(SRCDIR)/preprocessor.o: lib/preprocessor.cpp lib/cxx11emu.h lib/preprocessor.h lib/config.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/token.h lib/valueflow.h lib/mathlib.h lib/path.h lib/settings.h lib/library.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/preprocessor.o $(SRCDIR)/preprocessor.cpp $(SRCDIR)/settings.o: lib/settings.cpp lib/cxx11emu.h lib/settings.h lib/config.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/preprocessor.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/settings.o $(SRCDIR)/settings.cpp $(SRCDIR)/suppressions.o: lib/suppressions.cpp lib/cxx11emu.h lib/suppressions.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/suppressions.o $(SRCDIR)/suppressions.cpp $(SRCDIR)/symboldatabase.o: lib/symboldatabase.cpp lib/cxx11emu.h lib/symboldatabase.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/symboldatabase.o $(SRCDIR)/symboldatabase.cpp $(SRCDIR)/templatesimplifier.o: lib/templatesimplifier.cpp lib/cxx11emu.h lib/templatesimplifier.h lib/config.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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) -std=c++0x -c -o $(SRCDIR)/timer.o $(SRCDIR)/timer.cpp $(SRCDIR)/token.o: lib/token.cpp lib/cxx11emu.h lib/token.h lib/config.h lib/valueflow.h lib/mathlib.h lib/errorlogger.h lib/suppressions.h lib/check.h lib/tokenize.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/token.o $(SRCDIR)/token.cpp $(SRCDIR)/tokenize.o: lib/tokenize.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h lib/check.h lib/symboldatabase.h lib/templatesimplifier.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/tokenize.o $(SRCDIR)/tokenize.cpp $(SRCDIR)/tokenlist.o: lib/tokenlist.cpp lib/cxx11emu.h lib/tokenlist.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/path.h lib/preprocessor.h lib/settings.h lib/library.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/tokenlist.o $(SRCDIR)/tokenlist.cpp $(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/cxx11emu.h lib/valueflow.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/token.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/valueflow.o $(SRCDIR)/valueflow.cpp cli/cmdlineparser.o: cli/cmdlineparser.cpp lib/cxx11emu.h cli/cmdlineparser.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h cli/cppcheckexecutor.h cli/filelister.h lib/check.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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 cli/cmdlineparser.h lib/cppcheck.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h cli/filelister.h cli/pathmatch.h lib/preprocessor.h cli/threadexecutor.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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 $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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) -std=c++0x -c -o cli/main.o cli/main.cpp cli/pathmatch.o: cli/pathmatch.cpp lib/cxx11emu.h cli/pathmatch.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o cli/pathmatch.o cli/pathmatch.cpp cli/threadexecutor.o: cli/threadexecutor.cpp lib/cxx11emu.h cli/threadexecutor.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/cppcheck.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h cli/cppcheckexecutor.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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) -std=c++0x -c -o test/options.o test/options.cpp test/test64bit.o: test/test64bit.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/check64bit.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/test64bit.o test/test64bit.cpp test/testassert.o: test/testassert.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkassert.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testassert.o test/testassert.cpp test/testassignif.o: test/testassignif.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkassignif.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testassignif.o test/testassignif.cpp test/testautovariables.o: test/testautovariables.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkautovariables.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testautovariables.o test/testautovariables.cpp test/testbool.o: test/testbool.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkbool.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testbool.o test/testbool.cpp test/testboost.o: test/testboost.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkboost.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testboost.o test/testboost.cpp test/testbufferoverrun.o: test/testbufferoverrun.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkbufferoverrun.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testbufferoverrun.o test/testbufferoverrun.cpp test/testcharvar.o: test/testcharvar.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkother.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testcharvar.o test/testcharvar.cpp test/testclass.o: test/testclass.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkclass.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testclass.o test/testclass.cpp test/testcmdlineparser.o: test/testcmdlineparser.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/settings.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testcmdlineparser.o test/testcmdlineparser.cpp test/testconstructors.o: test/testconstructors.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkclass.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testconstructors.o test/testconstructors.cpp test/testcppcheck.o: test/testcppcheck.cpp lib/cxx11emu.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h test/testsuite.h test/redirect.h lib/check.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testcppcheck.o test/testcppcheck.cpp test/testdivision.o: test/testdivision.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkother.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testdivision.o test/testdivision.cpp test/testerrorlogger.o: test/testerrorlogger.cpp lib/cxx11emu.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testerrorlogger.o test/testerrorlogger.cpp test/testexceptionsafety.o: test/testexceptionsafety.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkexceptionsafety.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testexceptionsafety.o test/testexceptionsafety.cpp test/testfilelister.o: test/testfilelister.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/settings.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testfilelister.o test/testfilelister.cpp test/testincompletestatement.o: test/testincompletestatement.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/checkother.h lib/check.h lib/settings.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testincompletestatement.o test/testincompletestatement.cpp test/testinternal.o: test/testinternal.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkinternal.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testio.o test/testio.cpp test/testleakautovar.o: test/testleakautovar.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkleakautovar.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testleakautovar.o test/testleakautovar.cpp test/testlibrary.o: test/testlibrary.cpp lib/cxx11emu.h lib/library.h lib/config.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenlist.h test/testsuite.h lib/errorlogger.h lib/suppressions.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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 test/redirect.h lib/library.h lib/path.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testmathlib.o test/testmathlib.cpp test/testmemleak.o: test/testmemleak.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkmemoryleak.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h lib/symboldatabase.h lib/preprocessor.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testmemleak.o test/testmemleak.cpp test/testnonreentrantfunctions.o: test/testnonreentrantfunctions.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checknonreentrantfunctions.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testnonreentrantfunctions.o test/testnonreentrantfunctions.cpp test/testnullpointer.o: test/testnullpointer.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checknullpointer.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testnullpointer.o test/testnullpointer.cpp test/testobsoletefunctions.o: test/testobsoletefunctions.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkobsoletefunctions.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testobsoletefunctions.o test/testobsoletefunctions.cpp test/testoptions.o: test/testoptions.cpp lib/cxx11emu.h test/options.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testoptions.o test/testoptions.cpp test/testother.o: test/testother.cpp lib/cxx11emu.h lib/preprocessor.h lib/config.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/symboldatabase.h lib/token.h lib/valueflow.h lib/mathlib.h lib/checkother.h lib/check.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testother.o test/testother.cpp test/testpath.o: test/testpath.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testpath.o test/testpath.cpp test/testpathmatch.o: test/testpathmatch.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testpathmatch.o test/testpathmatch.cpp test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkpostfixoperator.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testpostfixoperator.o test/testpostfixoperator.cpp test/testpreprocessor.o: test/testpreprocessor.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/preprocessor.h lib/tokenize.h lib/tokenlist.h lib/settings.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testpreprocessor.o test/testpreprocessor.cpp test/testrunner.o: test/testrunner.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h test/options.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testrunner.o test/testrunner.cpp test/testsamples.o: test/testsamples.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testsamples.o test/testsamples.cpp test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/settings.h lib/standards.h lib/timer.h lib/templatesimplifier.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testsimplifytokens.o test/testsimplifytokens.cpp test/testsizeof.o: test/testsizeof.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checksizeof.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testsizeof.o test/testsizeof.cpp test/teststl.o: test/teststl.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkstl.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/teststl.o test/teststl.cpp test/testsuite.o: test/testsuite.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h test/options.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testsuite.o test/testsuite.cpp test/testsuppressions.o: test/testsuppressions.cpp lib/cxx11emu.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testsuppressions.o test/testsuppressions.cpp test/testsymboldatabase.o: test/testsymboldatabase.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h test/testutils.h lib/settings.h lib/standards.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/symboldatabase.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp test/testthreadexecutor.o: test/testthreadexecutor.cpp lib/cxx11emu.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testthreadexecutor.o test/testthreadexecutor.cpp test/testtimer.o: test/testtimer.cpp lib/cxx11emu.h lib/timer.h lib/config.h test/testsuite.h lib/errorlogger.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testtimer.o test/testtimer.cpp test/testtoken.o: test/testtoken.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h test/testutils.h lib/settings.h lib/standards.h lib/timer.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testtoken.o test/testtoken.cpp test/testtokenize.o: test/testtokenize.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/settings.h lib/standards.h lib/timer.h lib/preprocessor.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testtokenize.o test/testtokenize.cpp test/testuninitvar.o: test/testuninitvar.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkuninitvar.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testuninitvar.o test/testuninitvar.cpp test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h test/testsuite.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/checkunusedfunctions.h lib/check.h lib/settings.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testunusedfunctions.o test/testunusedfunctions.cpp test/testunusedprivfunc.o: test/testunusedprivfunc.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkclass.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testunusedprivfunc.o test/testunusedprivfunc.cpp test/testunusedvar.o: test/testunusedvar.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h lib/checkunusedvar.h lib/check.h lib/settings.h lib/standards.h lib/timer.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testunusedvar.o test/testunusedvar.cpp test/testvalueflow.o: test/testvalueflow.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/token.h lib/valueflow.h test/testutils.h lib/settings.h lib/standards.h lib/timer.h lib/tokenize.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o test/testvalueflow.o test/testvalueflow.cpp externals/tinyxml/tinyxml2.o: externals/tinyxml/tinyxml2.cpp lib/cxx11emu.h externals/tinyxml/tinyxml2.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o externals/tinyxml/tinyxml2.o externals/tinyxml/tinyxml2.cpp tools/dmake.o: tools/dmake.cpp lib/cxx11emu.h cli/filelister.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -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) -std=c++0x -c -o tools/reduce.o tools/reduce.cpp cppcheck-1.66/build-pcre.txt000066400000000000000000000036101236713773000160260ustar00rootroot00000000000000PCRE is a library that is used by the optional "rules" feature. (It adds some additional features to the command line client.) It's 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. - If you're not on Windows, 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 Some temporary build instructions. This is work in progress. Windows Visual Studio To build PCRE, download the source code from www.pcre.org and CMake (http://www.cmake.org/cmake/resources/software.html). Then I assume you use MSVC 2010 otherwise adapt the commands for your version. VS Solution file cmake . -G "Visual Studio 10" Open PCRE.sln with VS IDE or via cmd: call "%VS100COMNTOOLS%..\..\VC\vcvarsall.bat" x86 MSBuild PCRE.sln /target:Build /property:Configuration="Release" For 64-bit run: cmake . -G "Visual Studio 10 Win64" or using NMake call "%VS100COMNTOOLS%..\..\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.66/build.bat000066400000000000000000000031311236713773000150240ustar00rootroot00000000000000@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.66/cfg/000077500000000000000000000000001236713773000137765ustar00rootroot00000000000000cppcheck-1.66/cfg/avr.cfg000066400000000000000000000271261236713773000152570ustar00rootroot00000000000000 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.66/cfg/gtk.cfg000066400000000000000000017443141236713773000152620ustar00rootroot00000000000000 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_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_get_strv g_variant_get_objv 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.66/cfg/posix.cfg000066400000000000000000000142131236713773000156220ustar00rootroot00000000000000 false 0:999999 true false false false false false false false false false false false false false false false false false false false false false false 0: 0: false 0: false false 0: free strdup strndup strdupa strndupa wcsdup close open socket closedir opendir fdopendir fclose fdopen pclose popen cppcheck-1.66/cfg/qt.cfg000066400000000000000000000046631236713773000151140ustar00rootroot00000000000000 READ READ WRITE NOTIFY connect invokeMethod true true cppcheck-1.66/cfg/sdl.cfg000066400000000000000000000032071236713773000152430ustar00rootroot00000000000000 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.66/cfg/std.cfg000066400000000000000000000707171236713773000152650ustar00rootroot00000000000000 true 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 0: 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:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 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 0: false false 0: false 0: false false false false false false false false false false free malloc calloc realloc fclose fopen freopen false false false false false false false false false false false false 0: false 0: false 0: false 0: false 0: false false false false false false false false false false false false false false 0:255 false false 0,2:36 false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0:255 false 0:255 false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false false false false false false false false false false cppcheck-1.66/cfg/windows.cfg000066400000000000000000000352501236713773000161560ustar00rootroot00000000000000 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 CreateMutex CreateSemaphore CreateTimerQueue CreateWaitableTimer OpenEvent OpenMutex OpenSemaphore OpenWaitableTimer OpenJobObject OpenProcess OpenThread CreateMailslot CloseHandle OpenSCManager OpenService CreateService CloseServiceHandle LockServiceDatabase UnlockServiceDatabase 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 MapViewOfFile MapViewOfFileEx MapViewOfFileExNuma MapViewOfFileFromApp UnmapViewOfFile RtlCreateHeap RtlDestroyHeap wcsdup _strdup _wcsdup _mbsdup _malloc_dbg _aligned_malloc _aligned_offset_malloc _strdup_dbg _wcsdup_dbg free 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 cppcheck-1.66/cli/000077500000000000000000000000001236713773000140065ustar00rootroot00000000000000cppcheck-1.66/cli/cli.vcxproj000066400000000000000000000570431236713773000162030ustar00rootroot00000000000000 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 Application Unicode false v120_xp Application Unicode false v120_xp Application Unicode false v120 Application Unicode false v120 Application Unicode false v120_xp Application Unicode false v120_xp Application Unicode false v120 Application Unicode false v120 ..\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\tinyxml;%(AdditionalIncludeDirectories) false true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true $(TargetDir)cli.pdb ..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true ..\lib;..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true ..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true ..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true ..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true ..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true ..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true {c183db5b-ad6c-423d-80ca-1f9549555a1a} cppcheck-1.66/cli/cli.vcxproj.filters000066400000000000000000000040421236713773000176410ustar00rootroot00000000000000 {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 Header Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files cppcheck-1.66/cli/cmdlineparser.cpp000066400000000000000000001301531236713773000173450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "cppcheck.h" #include "cppcheckexecutor.h" #include "filelister.h" #include "path.h" #include "settings.h" #include "timer.h" #include "check.h" #include #include #include #include #include #include #include // EXIT_FAILURE #ifdef HAVE_RULES // xml is used in 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 // ideas : we could also require this should be an xml file, with the filenames // specified in an xml structure // we could elaborate this then, to also include the I-paths, ... // basically for everything that makes the command line very long // xml is a bonus then, since we can easily extend it // we need a good parser then -> suggestion : TinyXml // drawback : creates a dependency std::istream *Files; std::ifstream Infile; if (FileList.compare("-") == 0) { // read from stdin Files = &std::cin; } else { Infile.open(FileList.c_str()); Files = &Infile; } if (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::fromNativeSeparators(PathName); PathName = Path::removeQuotationMarks(PathName); // If path doesn't end with / or \, add it if (PathName[PathName.length()-1] != '/') 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; } bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) { bool def = false; bool maxconfigs = false; for (int i = 1; i < argc; i++) { if (std::strcmp(argv[i], "--version") == 0) { _showVersion = true; _exitAfterPrint = true; return true; } // Flag used for various purposes during debugging else if (std::strcmp(argv[i], "--debug") == 0) _settings->debug = _settings->debugwarnings = true; // Show debug warnings else if (std::strcmp(argv[i], "--debug-warnings") == 0) _settings->debugwarnings = true; // Print out code that triggers false positive else if (std::strcmp(argv[i], "--debug-fp") == 0) _settings->debugFalsePositive = 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); } // Inconclusive checking (still in testing phase) 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", 23) == 0) { std::string filename; // exitcode-suppressions filename.txt // Deprecated if (std::strcmp(argv[i], "--exitcode-suppressions") == 0) { ++i; if (i >= argc || std::strncmp(argv[i], "-", 1) == 0 || std::strncmp(argv[i], "--", 2) == 0) { PrintMessage("cppcheck: No filename specified for the '--exitcode-suppressions' option."); return false; } filename = argv[i]; } // exitcode-suppressions=filename.txt else { 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; } } // Filter errors // This is deprecated, see --suppressions-list above else if (std::strcmp(argv[i], "--suppressions") == 0) { ++i; if (i >= argc) { PrintMessage("cppcheck: No file specified for the '--suppressions' option."); return false; } std::ifstream f(argv[i]); if (!f.is_open()) { std::string message("cppcheck: Couldn't open the file: \""); message += std::string(argv[i]); message += "\"."; 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); std::string::size_type pos; do { pos = paths.find(';'); _settings->_basePaths.push_back(Path::fromNativeSeparators(paths.substr(0, pos))); paths.erase(0, pos+1); } while (pos != std::string::npos); } else { PrintMessage("cppcheck: No paths specified for the '" + std::string(argv[i]) + "' option."); return false; } } // 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 < 0 || _settings->_xml_version > 2) { // We only have xml versions 1 and 2 PrintMessage("cppcheck: '--xml-version' can only be 1 or 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->_errorsOnly = true; // Append userdefined code to checked source code else if (std::strncmp(argv[i], "--append=", 9) == 0) { const std::string filename = 9 + argv[i]; if (!_settings->append(filename)) { PrintMessage("cppcheck: Couldn't open the file: \"" + filename + "\"."); return false; } } // 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("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); } // 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::fromNativeSeparators(path); path = Path::removeQuotationMarks(path); // If path doesn't end with / or \, add it if (path[path.length()-1] != '/') 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::fromNativeSeparators(path); path = Path::simplifyPath(path); path = Path::removeQuotationMarks(path); if (FileLister::isDirectory(path)) { // If directory name doesn't end with / or \, add it if (path[path.length()-1] != '/') path += '/'; } _ignoredPaths.push_back(path); } } // --library else if (std::strncmp(argv[i], "--library=", 10) == 0) { Library::Error err = _settings->library.load(argv[0], argv[i]+10); 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::BAD_ELEMENT: errmsg = "Unexpected element"; break; case Library::MISSING_ATTRIBUTE: errmsg = "Missing attribute"; break; case Library::BAD_ATTRIBUTE: errmsg = "Bad attribute"; break; case Library::BAD_ATTRIBUTE_VALUE: errmsg = "Bad attribute value"; break; } if (!err.reason.empty()) errmsg += " '" + err.reason + "'"; if (!errmsg.empty()) { PrintMessage("cppcheck: Failed to load library configuration file '" + std::string(argv[i]+10) + "'. " + errmsg); 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; } // 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 += "\"."; 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_NO_ERROR) { 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->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 { 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 if (std::strncmp(argv[i], "-", 1) == 0 || std::strncmp(argv[i], "--", 2) == 0) { std::string message("cppcheck: error: unrecognized command line option: \""); message += argv[i]; message += "\"."; PrintMessage(message); return false; } else { std::string path = Path::fromNativeSeparators(argv[i]); path = Path::removeQuotationMarks(path); _pathnames.push_back(path); } } if (def && !_settings->_force && !maxconfigs) _settings->_maxConfigs = 1U; if (_settings->_force) _settings->_maxConfigs = ~0U; if (_settings->isEnabled("unusedFunction") && _settings->_jobs > 1) { PrintMessage("cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check."); } if (_settings->inconclusive && _settings->_xml && _settings->_xml_version == 1U) { PrintMessage("cppcheck: inconclusive messages will not be shown, because the old xml format is not compatible. It's recommended to use the new xml format (use --xml-version=2)."); } 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()) { 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" " --append= This allows you to provide information about functions\n" " by providing an implementation for them.\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" " --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" " --enable= Enable additional checks. The available ids are:\n" " * all\n" " Enable all checks. It is recommended to only\n" " use --enable=all when the whole program is\n" " scanned, because this enables unusedFunction.\n" " * warning\n" " Enable warning messages\n" " * style\n" " Enable all coding style checks. All messages\n" " with the severities 'style', 'performance' and\n" " 'portability' are enabled.\n" " * performance\n" " Enable performance messages\n" " * portability\n" " Enable portability messages\n" " * information\n" " Enable information messages\n" " * unusedFunction\n" " Check for unused functions. It is recommend\n" " to only enable this when the whole program is\n" " scanned.\n" " * missingInclude\n" " Warn if there are missing includes. For\n" " detailed information, use '--check-config'.\n" " Several ids can be given if you separate them with\n" " commas. See also --std\n" " --error-exitcode= If errors are found, integer [n] is returned instead of\n" " the default '0'. '" << EXIT_FAILURE << "' is returned\n" " if arguments are not valid or if no input files are\n" " provided. Note that your operating system can modify\n" " this value, e.g. '256' can become '0'.\n" " --errorlist Print a list of all the error messages in XML format.\n" " --exitcode-suppressions=\n" " Used when certain messages should be displayed but\n" " should not cause a non-zero exitcode.\n" " --file-list= Specify the files to check in a text file. Add one\n" " filename per line. When file is '-,' the file list will\n" " be read from standard input.\n" " -f, --force Force checking of all configurations in files. If used\n" " together with '--max-configs=', the last option is the\n" " one that is effective.\n" " -h, --help Print this help.\n" " -I Give path to search for include files. Give several -I\n" " parameters to give several paths. First given path is\n" " searched for contained header files first. If paths are\n" " relative to source files, this is not needed.\n" " --includes-file=\n" " Specify directory paths to search for included header\n" " files in a text file. Add one include path per line.\n" " First given path is searched for contained header\n" " files first. If paths are relative to source files,\n" " this is not needed.\n" " --config-exclude=\n" " Path (prefix) to be excluded from configuration checking.\n" " Preprocessor configurations defined in headers (but not sources)\n" " matching the prefix will not be considered for evaluation\n" " of configuration alternatives\n" " --config-excludes-file=\n" " A file that contains a list of config-excludes\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 [jobs] threads to do the checking simultaneously.\n" " -l Specifies that no new threads should be started if there\n" " are other threads running and the load average is at least\n" " load (ignored on non UNIX-like systems)\n" " --language=, -x \n" " Forces cppcheck to check all files as the given\n" " language. Valid values are: c, c++\n" " --library=\n" " Use library configuration.\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= Specifies platform specific types and sizes. The\n" " available 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" " -q, --quiet Only print error messages.\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" " https://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 (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 versions 1 and\n" " 2 are available. The default version is 1." "\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.sourceforge.net/manual.pdf\n"; } cppcheck-1.66/cli/cmdlineparser.h000066400000000000000000000056651236713773000170230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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); private: std::vector _pathnames; std::vector _ignoredPaths; Settings *_settings; bool _showHelp; bool _showVersion; bool _showErrorMessages; bool _exitAfterPrint; }; /// @} #endif // CMDLINE_PARSER_H cppcheck-1.66/cli/cppcheckexecutor.cpp000066400000000000000000000753021236713773000200600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "cmdlineparser.h" #include "cppcheck.h" #include "errorlogger.h" #include "filelister.h" #include "path.h" #include "pathmatch.h" #include "preprocessor.h" #include "threadexecutor.h" #include #include // EXIT_SUCCESS and EXIT_FAILURE #include #include #include #include #if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(__OS2__) #define USE_UNIX_SIGNAL_HANDLING #include #include #endif #if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(USE_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(__SVR4) #define USE_UNIX_BACKTRACE_SUPPORT #include #include #endif #if defined(_MSC_VER) #define USE_WINDOWS_SEH #include #include #include #include #endif CppCheckExecutor::CppCheckExecutor() : _settings(0), time1(0), errorlist(false) { } CppCheckExecutor::~CppCheckExecutor() { } 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 * 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(settings._xml_version); cppcheck->getErrorMessages(); std::cout << ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version) << 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 (unless --quiet // was used) and remove the non-existing path from the list. if (!settings._errorsOnly) std::cout << "cppcheck: warning: Couldn't find path given by -I '" << path << '\'' << std::endl; iter = settings._includePaths.erase(iter); } } } const std::vector& pathnames = parser.GetPathNames(); if (!pathnames.empty()) { // Execute recursiveAddFiles() to each given file parameter std::vector::const_iterator iter; for (iter = pathnames.begin(); iter != pathnames.end(); ++iter) FileLister::recursiveAddFiles(_files, Path::toNativeSeparators(*iter), _settings->library.markupExtensions()); } if (!_files.empty()) { // Remove header files from the list of ignored files. // Also output a warning for the user. // TODO: Remove all unknown files? (use FileLister::acceptFile()) bool warn = false; std::vector ignored = parser.GetIgnoredPaths(); for (std::vector::iterator i = ignored.begin(); i != ignored.end();) { const std::string extension = Path::getFilenameExtension(*i); if (extension == ".h" || extension == ".hpp") { i = ignored.erase(i); warn = true; } else ++i; } 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; } #if defined(_WIN32) // For Windows we want case-insensitive path matching const bool caseSensitive = false; #else const bool caseSensitive = true; #endif PathMatch matcher(parser.GetIgnoredPaths(), caseSensitive); for (std::map::iterator i = _files.begin(); i != _files.end();) { if (matcher.Match(i->first)) _files.erase(i++); else ++i; } } else { std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl; return false; } if (!_files.empty()) { return true; } else { std::cout << "cppcheck: error: no files to check - all paths ignored." << std::endl; return false; } } int CppCheckExecutor::check(int argc, const char* const argv[]) { Preprocessor::missingIncludeFlag = false; Preprocessor::missingSystemIncludeFlag = false; CppCheck cppCheck(*this, true); 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); } } /** * Simple helper function: * \return size of array * */ template size_t GetArrayLength(const T(&)[size]) { return size; } #if defined(USE_UNIX_SIGNAL_HANDLING) /* (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: */ struct Signaltype { int signalnumber; const char *signalname; }; #define DECLARE_SIGNAL(x) {x, #x} static const Signaltype listofsignals[] = { // don't care: SIGABRT, DECLARE_SIGNAL(SIGBUS), DECLARE_SIGNAL(SIGFPE), DECLARE_SIGNAL(SIGILL), DECLARE_SIGNAL(SIGINT), DECLARE_SIGNAL(SIGSEGV), // don't care: SIGTERM }; /* * Simple mapping */ static const char *signal_name(int signo) { for (size_t s=0; s(firstBracketName+1))) { char input_buffer[512]= {0}; strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1); char output_buffer[1024]= {0}; size_t length = GetArrayLength(output_buffer); int status=0; realname = abi::__cxa_demangle(input_buffer, output_buffer, &length, &status); // non-NULL on success } } const int ordinal=i-offset; fprintf(f, "#%-2d 0x", ordinal); if (padLen>0) fprintf(f, "%0*d", padLen, 0); if (realname) { fprintf(f, "%.*s in %s\n", (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, realname); } else { fprintf(f, "%.*s in %.*s\n", (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, (int)(firstBracketAddress-symbol), symbol); } } free(symbolstrings); } else { fputs("Callstack could not be obtained\n", f); } #endif } /* * Entry pointer for signal handlers */ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * /*context*/) { const char * const signame = signal_name(signo); const char * const sigtext = strsignal(signo); bool bPrintCallstack=true; FILE* f=CppCheckExecutor::getExceptionOutput()=="stderr" ? stderr : stdout; fputs("Internal error: cppcheck received signal ", f); fputs(signame, f); fputs(", ", f); fputs(sigtext, f); switch (signo) { case SIGBUS: switch (info->si_code) { case BUS_ADRALN: // invalid address alignment fprintf(f, " - BUS_ADRALN"); break; case BUS_ADRERR: // nonexistent physical address fprintf(f, " - BUS_ADRERR"); break; case BUS_OBJERR: // object-specific hardware error fprintf(f, " - BUS_OBJERR"); break; #ifdef BUS_MCEERR_AR case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check; fprintf(f, " - BUS_MCEERR_AR"); break; #endif #ifdef BUS_MCEERR_AO case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed fprintf(f, " - BUS_MCEERR_AO"); break; #endif default: break; } fprintf(f, " (at 0x%p).\n", info->si_addr); break; case SIGFPE: switch (info->si_code) { case FPE_INTDIV: // integer divide by zero fprintf(f, " - FPE_INTDIV"); break; case FPE_INTOVF: // integer overflow fprintf(f, " - FPE_INTOVF"); break; case FPE_FLTDIV: // floating-point divide by zero fprintf(f, " - FPE_FLTDIV"); break; case FPE_FLTOVF: // floating-point overflow fprintf(f, " - FPE_FLTOVF"); break; case FPE_FLTUND: // floating-point underflow fprintf(f, " - FPE_FLTUND"); break; case FPE_FLTRES: // floating-point inexact result fprintf(f, " - FPE_FLTRES"); break; case FPE_FLTINV: // floating-point invalid operation fprintf(f, " - FPE_FLTINV"); break; case FPE_FLTSUB: // subscript out of range fprintf(f, " - FPE_FLTSUB"); break; default: break; } fprintf(f, " (at 0x%p).\n", info->si_addr); break; case SIGILL: switch (info->si_code) { case ILL_ILLOPC: // illegal opcode fprintf(f, " - ILL_ILLOPC"); break; case ILL_ILLOPN: // illegal operand fprintf(f, " - ILL_ILLOPN"); break; case ILL_ILLADR: // illegal addressing mode fprintf(f, " - ILL_ILLADR"); break; case ILL_ILLTRP: // illegal trap fprintf(f, " - ILL_ILLTRP"); break; case ILL_PRVOPC: // privileged opcode fprintf(f, " - ILL_PRVOPC"); break; case ILL_PRVREG: // privileged register fprintf(f, " - ILL_PRVREG"); break; case ILL_COPROC: // coprocessor error fprintf(f, " - ILL_COPROC"); break; case ILL_BADSTK: // internal stack error fprintf(f, " - ILL_BADSTK"); break; default: break; } fprintf(f, " (at 0x%p).\n", info->si_addr); break; case SIGINT: bPrintCallstack=false; fprintf(f, ".\n"); break; case SIGSEGV: switch (info->si_code) { case SEGV_MAPERR: // address not mapped to object fprintf(f, " - SEGV_MAPERR"); break; case SEGV_ACCERR: // invalid permissions for mapped object fprintf(f, " - SEGV_ACCERR"); break; default: break; } fprintf(f, " (at 0x%p).\n", info->si_addr); break; default: fputs(".\n", f); break; } if (bPrintCallstack) { print_stacktrace(f, true); fputs("\nPlease report this to the cppcheck developers!\n", f); } abort(); } #endif #ifdef USE_WINDOWS_SEH static 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); static fpStackWalk64 pStackWalk64; typedef DWORD64(WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); static fpSymGetModuleBase64 pSymGetModuleBase64; typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); static fpSymGetSymFromAddr64 pSymGetSymFromAddr64; typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64); static fpSymGetLineFromAddr64 pSymGetLineFromAddr64; typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD) ; static fpUnDecorateSymbolName pUnDecorateSymbolName; typedef PVOID(WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); static fpSymFunctionTableAccess64 pSymFunctionTableAccess64; typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL); static fpSymInitialize pSymInitialize; static HMODULE hLibDbgHelp; // avoid explicit dependency on Dbghelp.dll static bool loadDbgHelp() { hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll"); if (!hLibDbgHelp) return true; 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; } static void PrintCallstack(FILE* f, 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, NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL ); 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(f, "%lu. 0x%08LX in ", frame, (ULONG64)stack.AddrPC.Offset); fputs((const char *)undname, f); fputs("\n", f); if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end... break; } FreeLibrary(hLibDbgHelp); hLibDbgHelp=0; } /* * Any evaluation of the information about the exception needs to be done here! */ static int filterException(int code, PEXCEPTION_POINTERS ex) { FILE *f = stdout; fputs("Internal error: ", f); switch (ex->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: fputs("Access violation", f); switch (ex->ExceptionRecord->ExceptionInformation[0]) { case 0: fprintf(f, " reading from 0x%x", ex->ExceptionRecord->ExceptionInformation[1]); break; case 1: fprintf(f, " writing at 0x%x", ex->ExceptionRecord->ExceptionInformation[1]); break; case 8: fprintf(f, " data execution prevention at 0x%x", ex->ExceptionRecord->ExceptionInformation[1]); break; default: break; } break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: fputs("Out of array bounds", f); break; case EXCEPTION_BREAKPOINT: fputs("Breakpoint", f); break; case EXCEPTION_DATATYPE_MISALIGNMENT: fputs("Misaligned data", f); break; case EXCEPTION_FLT_DENORMAL_OPERAND: fputs("Denormalized floating-point value", f); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: fputs("Floating-point divide-by-zero", f); break; case EXCEPTION_FLT_INEXACT_RESULT: fputs("Inexact floating-point value", f); break; case EXCEPTION_FLT_INVALID_OPERATION: fputs("Invalid floating-point operation", f); break; case EXCEPTION_FLT_OVERFLOW: fputs("Floating-point overflow", f); break; case EXCEPTION_FLT_STACK_CHECK: fputs("Floating-point stack overflow", f); break; case EXCEPTION_FLT_UNDERFLOW: fputs("Floating-point underflow", f); break; case EXCEPTION_GUARD_PAGE: fputs("Page-guard access", f); break; case EXCEPTION_ILLEGAL_INSTRUCTION: fputs("Illegal instruction", f); break; case EXCEPTION_IN_PAGE_ERROR: fputs("Invalid page access", f); switch (ex->ExceptionRecord->ExceptionInformation[0]) { case 0: fprintf(f, " reading from 0x%x", ex->ExceptionRecord->ExceptionInformation[1]); break; case 1: fprintf(f, " writing at 0x%x", ex->ExceptionRecord->ExceptionInformation[1]); break; case 8: fprintf(f, " data execution prevention at 0x%x", ex->ExceptionRecord->ExceptionInformation[1]); break; default: break; } break; case EXCEPTION_INT_DIVIDE_BY_ZERO: fputs("Integer divide-by-zero", f); break; case EXCEPTION_INT_OVERFLOW: fputs("Integer overflow", f); break; case EXCEPTION_INVALID_DISPOSITION: fputs("Invalid exception dispatcher", f); break; case EXCEPTION_INVALID_HANDLE: fputs("Invalid handle", f); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: fputs("Non-continuable exception", f); break; case EXCEPTION_PRIV_INSTRUCTION: fputs("Invalid instruction", f); break; case EXCEPTION_SINGLE_STEP: fputs("Single instruction step", f); break; case EXCEPTION_STACK_OVERFLOW: fputs("Stack overflow", f); break; default: fprintf(f, "Unknown exception (%d)\n", code); break; } fputs("\n", f); PrintCallstack(f, ex); 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 *f = 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", f); return -1; } #elif defined(USE_UNIX_SIGNAL_HANDLING) struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags=SA_SIGINFO; act.sa_sigaction=CppcheckSignalHandler; for (std::size_t s=0; s callstack; const std::string msg("Failed to load " + std::string(!std ? "std.cfg" : "posix.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, Severity::information, msg+" "+details, "failedToLoadCfg", false); reportErr(errmsg); return EXIT_FAILURE; } if (settings.reportProgress) time1 = std::time(0); if (settings._xml) { reportErr(ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version)); } unsigned int returnValue = 0; if (settings._jobs == 1) { // Single process 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._errorsOnly) reportStatus(c + 1, _files.size(), processedsize, totalfilesize); c++; } } // 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._errorsOnly) reportStatus(c + 1, _files.size(), processedsize, totalfilesize); c++; } } cppcheck.checkFunctionUsage(); } 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(); } if (settings.isEnabled("information") || settings.checkConfiguration) reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions()); if (!settings.checkConfiguration) { cppcheck.tooManyConfigsError("",0U); if (settings.isEnabled("missingInclude") && (Preprocessor::missingIncludeFlag || Preprocessor::missingSystemIncludeFlag)) { const std::list callStack; ErrorLogger::ErrorMessage msg(callStack, 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._xml_version)); } _settings = 0; if (returnValue) return settings._exitCode; else return 0; } void CppCheckExecutor::reportErr(const std::string &errmsg) { // Alert only about unique errors if (_errorList.find(errmsg) != _errorList.end()) return; _errorList.insert(errmsg); std::cerr << errmsg << std::endl; } void CppCheckExecutor::reportOut(const std::string &outmsg) { std::cout << outmsg << std::endl; } void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value) { (void)filename; if (!time1) return; // Report progress messages every 10 seconds const std::time_t time2 = std::time(NULL); if (time2 >= (time1 + 10)) { time1 = time2; // 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; oss << fileindex << '/' << filecount << " files checked " << (sizetotal > 0 ? static_cast(static_cast(sizedone) / sizetotal*100) : 0) << "% done"; std::cout << oss.str() << std::endl; } } void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg) { if (errorlist) { reportOut(msg.toXML(false, _settings->_xml_version)); } else if (_settings->_xml) { reportErr(msg.toXML(_settings->_verbose, _settings->_xml_version)); } else { reportErr(msg.toString(_settings->_verbose, _settings->_outputFormat)); } } void CppCheckExecutor::setExceptionOutput(const std::string& fn) { exceptionOutput=fn; } const std::string& CppCheckExecutor::getExceptionOutput() { return exceptionOutput; } std::string CppCheckExecutor::exceptionOutput; cppcheck-1.66/cli/cppcheckexecutor.h000066400000000000000000000120661236713773000175230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 class CppCheck; 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 fn file name to be used from exception handler */ static void setExceptionOutput(const std::string& fn); /** * @return file name to be used for output from exception handler */ static const std::string& getExceptionOutput(); 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[]); 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 time1; /** * Output file name for exception handler */ static std::string exceptionOutput; /** * Has --errorlist been given? */ bool errorlist; }; #endif // CPPCHECKEXECUTOR_H cppcheck-1.66/cli/filelister.cpp000066400000000000000000000206431236713773000166610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #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 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[ cleanedPath.size()-1 ]; 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 (strchr(ansiFfd,'?')) { ansiFfd = ffd.cAlternateFileName; } const std::string fname(basedir + ansiFfd); if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // File const std::string nativename = Path::fromNativeSeparators(fname); if (!checkAllFilesInDir || Path::acceptFile(fname, extra)) { // 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 FileLister::recursiveAddFiles(files, fname, extra); } } while (FindNextFileA(hFind, &ffd) != FALSE); if (INVALID_HANDLE_VALUE != hFind) { 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 #include #include #include // Get absolute path. Returns empty string if path does not exist or other error. std::string FileLister::getAbsolutePath(const std::string& path) { std::string absolute_path; #ifdef PATH_MAX char buf[PATH_MAX]; if (realpath(path.c_str(), buf) != NULL) absolute_path = buf; #else char *dynamic_buf; if ((dynamic_buf = realpath(path.c_str(), NULL)) != NULL) { absolute_path = dynamic_buf; free(dynamic_buf); } #endif return absolute_path; } void FileLister::recursiveAddFiles2(std::set &seen_paths, std::map &files, const std::string &path, const std::set &extra) { std::ostringstream oss; oss << path; if (path.length() > 0 && path[path.length()-1] == '/') oss << "*"; glob_t glob_results; glob(oss.str().c_str(), GLOB_MARK, 0, &glob_results); for (unsigned int i = 0; i < glob_results.gl_pathc; i++) { const std::string filename = glob_results.gl_pathv[i]; if (filename == "." || filename == ".." || filename.length() == 0) continue; // Determine absolute path. Empty filename if path does not exist const std::string absolute_path = getAbsolutePath(filename); if (absolute_path.empty()) continue; // Did we already process this entry? if (seen_paths.find(absolute_path) != seen_paths.end()) continue; if (filename[filename.length()-1] != '/') { // File if (Path::sameFileName(path,filename) || Path::acceptFile(filename, extra)) { seen_paths.insert(absolute_path); struct stat sb; if (stat(absolute_path.c_str(), &sb) == 0) { // Limitation: file sizes are assumed to fit in a 'size_t' files[filename] = static_cast(sb.st_size); } else files[filename] = 0; } } else { // Directory seen_paths.insert(absolute_path); recursiveAddFiles2(seen_paths, files, filename, extra); } } globfree(&glob_results); } void FileLister::recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra) { std::set seen_paths; recursiveAddFiles2(seen_paths, files, path, extra); } bool FileLister::isDirectory(const std::string &path) { bool ret = false; glob_t glob_results; glob(path.c_str(), GLOB_MARK, 0, &glob_results); if (glob_results.gl_pathc == 1) { const std::string glob_path = glob_results.gl_pathv[0]; if (!glob_path.empty() && glob_path[glob_path.size() - 1] == '/') { ret = true; } } globfree(&glob_results); return ret; } bool FileLister::fileExists(const std::string &path) { struct stat statinfo; int result = stat(path.c_str(), &statinfo); if (result < 0) { // Todo: should check errno == ENOENT? // File not found return false; } // Check if file is regular file if ((statinfo.st_mode & S_IFMT) == S_IFREG) return true; return false; } #endif cppcheck-1.66/cli/filelister.h000066400000000000000000000054351236713773000163300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 /// @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 */ static void recursiveAddFiles(std::map &files, const std::string &path) { const std::set extra; recursiveAddFiles(files, path, extra); } /** * @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 */ static void recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra); /** * @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); #ifndef _WIN32 static std::string getAbsolutePath(const std::string& path); static void recursiveAddFiles2(std::set &seen_paths, std::map &files, const std::string &path, const std::set &extra); #endif }; /// @} #endif // #ifndef filelisterH cppcheck-1.66/cli/main.cpp000066400000000000000000000121051236713773000154350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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.66 * * @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 = Information about functions * - Value flow analysis => 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()) { if (Token::Match(tok, "/ 0")) 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::simplifyTokenList) * -# Run the runSimplifiedChecks of all check classes * * When errors are found, they are reported back to the CppCheckExecutor through the ErrorLogger interface */ #include "cppcheckexecutor.h" #ifdef _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(NULL, exename, sizeof(exename)/sizeof(exename[0])-1); argv[0] = exename; #endif return exec.check(argc, argv); } // 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.66/cli/pathmatch.cpp000066400000000000000000000056221236713773000164700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include PathMatch::PathMatch(const std::vector &masks, bool caseSensitive) : _masks(masks), _caseSensitive(caseSensitive) { if (!_caseSensitive) for (std::vector::iterator i = _masks.begin(); i != _masks.end(); ++i) std::transform(i->begin(), i->end(), i->begin(), ::tolower); } bool PathMatch::Match(const std::string &path) const { if (path.empty()) return false; for (std::vector::const_iterator iterMask = _masks.begin(); iterMask != _masks.end(); ++iterMask) { const std::string& mask(*iterMask); std::string findpath(path); if (!_caseSensitive) std::transform(findpath.begin(), findpath.end(), findpath.begin(), ::tolower); // Filtering directory name if (mask[mask.length() - 1] == '/') { if (findpath[findpath.length() - 1] != '/') findpath = RemoveFilename(findpath); if (mask.length() > findpath.length()) continue; // Match relative paths starting with mask // -isrc matches src/foo.cpp if (findpath.compare(0, mask.size(), mask) == 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("/" + mask) != std::string::npos) return true; } // Filtering filename else { if (mask.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() - mask.size(), findpath.size(), mask) == 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.66/cli/pathmatch.h000066400000000000000000000033431236713773000161330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include /// @addtogroup CLI /// @{ /** * @brief Simple path matching for ignoring paths in CLI. */ class PathMatch { public: /** * The constructor. * @param masks List of masks. * @param caseSensitive Match the case of the characters when * matching paths? */ PathMatch(const std::vector &masks, 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 _masks; bool _caseSensitive; }; /// @} #endif // PATHMATCH_H cppcheck-1.66/cli/threadexecutor.cpp000066400000000000000000000423741236713773000175520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "cppcheck.h" #include "cppcheckexecutor.h" #include #ifdef __SVR4 // Solaris #include #endif #ifdef THREADING_MODEL_FORK #include #include #include #include #include #include #include #include #include #include #include #endif #ifdef THREADING_MODEL_WIN #include #include #include #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) { #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); } char *buf = new char[len]; if (read(rpipe, buf, len) <= 0) { std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(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 nchilds) { #ifdef __CYGWIN__ // getloadavg() is unsupported on Cygwin. return true; #else if (!nchilds || !_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 i = _files.begin(); for (;;) { // Start a new child size_t nchilds = rpipes.size(); if (i != _files.end() && nchilds < _settings._jobs && checkLoadAverage(nchilds)) { 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 (!_fileContents.empty() && _fileContents.find(i->first) != _fileContents.end()) { // File content was given as a string resultOfCheck = fileChecker.check(i->first, _fileContents[ i->first ]); } else { // Read file from a file resultOfCheck = fileChecker.check(i->first); } std::ostringstream oss; oss << resultOfCheck; writeToPipe(CHILD_END, oss.str()); std::exit(0); } close(pipes[1]); rpipes.push_back(pipes[0]); childFile[pid] = i->first; pipeFile[pipes[0]] = i->first; ++i; } 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, NULL, NULL, &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._errorsOnly) CppCheckExecutor::reportStatus(_fileCount, _files.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, 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 = 0; 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(); _processedFiles = 0; _processedSize = 0; _totalFiles = _files.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(NULL, 0, threadProc, this, 0, NULL); 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 &it = threadExecutor->_itNextFile; // guard static members of CppCheck against concurrent access EnterCriticalSection(&threadExecutor->_fileSync); CppCheck fileChecker(*threadExecutor, false); fileChecker.settings() = threadExecutor->_settings; LeaveCriticalSection(&threadExecutor->_fileSync); for (;;) { EnterCriticalSection(&threadExecutor->_fileSync); if (it == threadExecutor->_files.end()) { LeaveCriticalSection(&threadExecutor->_fileSync); return result; } const std::string &file = it->first; const std::size_t fileSize = it->second; ++it; 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); } EnterCriticalSection(&threadExecutor->_fileSync); threadExecutor->_processedSize += fileSize; threadExecutor->_processedFiles++; if (!threadExecutor->_settings._errorsOnly) { EnterCriticalSection(&threadExecutor->_reportSync); CppCheckExecutor::reportStatus(threadExecutor->_processedFiles, threadExecutor->_totalFiles, threadExecutor->_processedSize, threadExecutor->_totalFileSize); LeaveCriticalSection(&threadExecutor->_reportSync); } LeaveCriticalSection(&threadExecutor->_fileSync); }; #ifdef _MSC_VER #pragma warning(push) #pragma warning( disable : 4702 ) #endif return result; #ifdef _MSC_VER #pragma warning(pop) #endif } 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.66/cli/threadexecutor.h000066400000000000000000000103621236713773000172070ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include "errorlogger.h" #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 nchilds - count of currently runned childs * @return true - if new process can be started */ bool checkLoadAverage(size_t nchilds); 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::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.66/cli/version.rc000066400000000000000000000016441236713773000160260ustar00rootroot00000000000000#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.66/console_common.pri000066400000000000000000000010731236713773000167660ustar00rootroot00000000000000# 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.66/cppcheck.cbp000066400000000000000000000213671236713773000155160ustar00rootroot00000000000000 cppcheck-1.66/cppcheck.cppcheck000066400000000000000000000007731236713773000165300ustar00rootroot00000000000000 cppcheck-1.66/cppcheck.sln000066400000000000000000000122551236713773000155420ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio Express 2013 for Windows Desktop 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.66/createrelease000077500000000000000000000042501236713773000157720ustar00rootroot00000000000000#!/bin/bash # # A script for creating release packages. The release packages are create in the home directory. # # Test cppcheck on itself. # cppcheck -q -j2 --inconclusive --enable=all lib # # 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' | sort -u # # Update version numbers in: # sed -i "s/1.[0-9][0-9].99/1.62/" cli/main.cpp # sed -i "s/1,[0-9][0-9],99,0/1,62,0,0/" lib/version.h # sed -i "s/1.[0-9][0-9] dev/1.62/" lib/version.h # sed -i "s/1.[0-9][0-9] dev/1.62/" man/manual.docbook # sed -i "s/1.[0-9][0-9] dev/1.62/" win_installer/productInfo.wxi # sed -i "s/1.[0-9][0-9].99/1.62/" 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" # # Generate the manual.pdf, manual.html and version.txt # make # ./cppcheck --version > version.txt # docbook2pdf man/manual.docbook # xsltproc -o manual.html /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl man/manual.docbook # # Upload manual.pdf , manual.html and version.txt... # sftp danielmarjamaki,cppcheck@web.sourceforge.net # # save "cppcheck --doc" output on wiki # # compile new democlient: # ssh -t danielmarjamaki,cppcheck@shell.sourceforge.net # ./build.sh 1.43 # 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/ cppcheck-1.66/democlient/000077500000000000000000000000001236713773000153625ustar00rootroot00000000000000cppcheck-1.66/democlient/build.sh000077500000000000000000000013101236713773000170130ustar00rootroot00000000000000#!/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.66/democlient/democlient.cpp000066400000000000000000000050261236713773000202140ustar00rootroot00000000000000#include #include #include #include #include #include "cppcheck.h" static void unencode(const char *src, char *dest) { for (; *src; src++, dest++) { if (*src == '+') *dest = ' '; else if (*src == '%') { int code; if (sscanf(src+1, "%2x", &code) != 1) code = '?'; *dest = code; src += 2; } else *dest = *src; } *dest = '\0'; } class CppcheckExecutor : public ErrorLogger { private: const std::time_t stoptime; CppCheck cppcheck; public: CppcheckExecutor() : ErrorLogger() , stoptime(std::time(NULL)+2U) , cppcheck(*this,false) { cppcheck.settings().addEnabled("all"); cppcheck.settings().inconclusive = true; } void run(const char code[]) { cppcheck.check("test.c", code); } void reportOut(const std::string &outmsg) { } void reportErr(const ErrorLogger::ErrorMessage &msg) { printf("%s\n", msg.toString(true).c_str()); } void reportProgress(const std::string &filename, const char stage[], const unsigned int value) { if (std::time(NULL) >= stoptime) { printf("time to analyse the " "code is more than 1 " "second. terminating." "\n\n"); cppcheck.terminate(); } } }; int main() { char data[4096] = {0}; const char *query_string = getenv("QUERY_STRING"); if (query_string) std::strncpy(data, query_string, sizeof(data)-2); const char *lenstr = getenv("CONTENT_LENGTH"); if (lenstr) { int len = std::min(1 + atoi(lenstr), (int)(sizeof(data) - 2)); fgets(data, len, stdin); } char code[4096] = {0}; unencode(data, code); if (strlen(code) > 1000) { puts("Content-type: text/html\r\n\r\n"); puts("For performance reasons the code must be shorter than 1000 chars."); return EXIT_SUCCESS; } FILE *logfile = fopen("democlient.log", "at"); if (logfile != NULL) { fprintf(logfile, "===========================================================\n%s\n", code); fclose(logfile); } puts("Content-type: text/html\r\n\r\n"); puts("
");

    CppcheckExecutor cppcheckExecutor;
    cppcheckExecutor.run(code);

    puts("
Done!"); return EXIT_SUCCESS; } cppcheck-1.66/doxyfile000066400000000000000000002365151236713773000150210ustar00rootroot00000000000000# 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 necessairy 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.66/externals/000077500000000000000000000000001236713773000152445ustar00rootroot00000000000000cppcheck-1.66/externals/tinyxml/000077500000000000000000000000001236713773000167505ustar00rootroot00000000000000cppcheck-1.66/externals/tinyxml/tinyxml.pri000066400000000000000000000001101236713773000211600ustar00rootroot00000000000000HEADERS += $${BASEPATH}tinyxml2.h SOURCES += $${BASEPATH}tinyxml2.cpp cppcheck-1.66/externals/tinyxml/tinyxml2.cpp000077500000000000000000001522761236713773000212620ustar00rootroot00000000000000/* 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. # ifdef ANDROID_NDK # include #else # include #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; #define DELETE_NODE( node ) { \ if ( node ) { \ MemPool* pool = node->_memPool; \ node->~XMLNode(); \ pool->Free( node ); \ } \ } #define DELETE_ATTRIBUTE( attrib ) { \ if ( attrib ) { \ MemPool* pool = attrib->_memPool; \ attrib->~XMLAttribute(); \ pool->Free( attrib ); \ } \ } 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::Reset() { if ( _flags & NEEDS_DELETE ) { delete [] _start; } _flags = 0; _start = 0; _end = 0; } void StrPair::SetStr( const char* str, int flags ) { Reset(); size_t len = strlen( str ); _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 ) { TIXMLASSERT( endTag && *endTag ); char* start = p; // fixme: hides a member 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; } ++p; } return 0; } char* StrPair::ParseName( char* p ) { char* start = p; if ( !start || !(*start) ) { return 0; } while( *p && ( p == start ? XMLUtil::IsNameStartChar( *p ) : XMLUtil::IsNameChar( *p ) )) { ++p; } if ( p > start ) { Set( start, p, 0 ); return p; } return 0; } void StrPair::CollapseWhitespace() { // Trim leading space. _start = XMLUtil::SkipWhiteSpace( _start ); if ( _start && *_start ) { char* p = _start; // the read pointer char* q = _start; // the write pointer while( *p ) { if ( XMLUtil::IsWhiteSpace( *p )) { p = XMLUtil::SkipWhiteSpace( p ); 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() { if ( _flags & NEEDS_FLUSH ) { *_end = 0; _flags ^= NEEDS_FLUSH; if ( _flags ) { 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; } else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { if ( *(p+1) == CR ) { p += 2; } else { ++p; } *q++ = LF; } 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) == '#' ) { char buf[10] = { 0 }; int len; p = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); for( int i=0; i(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; } 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 covert this correctly anyway. return; } output += *length; // Scary scary fall throughs. switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); default: break; } } 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; ptrdiff_t delta = 0; unsigned mult = 1; if ( *(p+2) == 'x' ) { // Hexadecimal. if ( !*(p+3) ) { return 0; } const char* q = p+3; q = strchr( q, ';' ); if ( !q || !*q ) { return 0; } delta = q-p; --q; while ( *q != 'x' ) { if ( *q >= '0' && *q <= '9' ) { ucs += mult * (*q - '0'); } else if ( *q >= 'a' && *q <= 'f' ) { ucs += mult * (*q - 'a' + 10); } else if ( *q >= 'A' && *q <= 'F' ) { ucs += mult * (*q - 'A' + 10 ); } else { return 0; } mult *= 16; --q; } } else { // Decimal. if ( !*(p+2) ) { return 0; } const char* q = p+2; q = strchr( q, ';' ); if ( !q || !*q ) { return 0; } delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) { ucs += mult * (*q - '0'); } else { return 0; } 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, "%d", v ? 1 : 0 ); } /* 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 ); } 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; } char* XMLDocument::Identify( char* p, XMLNode** node ) { XMLNode* returnNode = 0; char* start = p; p = XMLUtil::SkipWhiteSpace( p ); if( !p || !*p ) { return p; } // What is this thing? // These strings define the matching patters: static const char* xmlHeader = { "_memPool = &_commentPool; p += xmlHeaderLen; } else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { returnNode = new (_commentPool.Alloc()) XMLComment( this ); returnNode->_memPool = &_commentPool; p += commentHeaderLen; } else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { XMLText* text = new (_textPool.Alloc()) XMLText( this ); returnNode = text; returnNode->_memPool = &_textPool; p += cdataHeaderLen; text->SetCData( true ); } else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); returnNode->_memPool = &_commentPool; p += dtdHeaderLen; } else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { returnNode = new (_elementPool.Alloc()) XMLElement( this ); returnNode->_memPool = &_elementPool; p += elementHeaderLen; } else { returnNode = new (_textPool.Alloc()) XMLText( this ); returnNode->_memPool = &_textPool; p = start; // Back it up, all the text counts. } *node = returnNode; return p; } bool XMLDocument::Accept( XMLVisitor* visitor ) const { 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 ), _firstChild( 0 ), _lastChild( 0 ), _prev( 0 ), _next( 0 ), _memPool( 0 ) { } XMLNode::~XMLNode() { DeleteChildren(); if ( _parent ) { _parent->Unlink( this ); } } const char* XMLNode::Value() const { return _value.GetStr(); } void XMLNode::SetValue( const char* str, bool staticMem ) { if ( staticMem ) { _value.SetInternedStr( str ); } else { _value.SetStr( str ); } } void XMLNode::DeleteChildren() { while( _firstChild ) { XMLNode* node = _firstChild; Unlink( node ); DELETE_NODE( node ); } _firstChild = _lastChild = 0; } void XMLNode::Unlink( XMLNode* child ) { 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->_parent = 0; } void XMLNode::DeleteChild( XMLNode* node ) { TIXMLASSERT( node->_parent == this ); DELETE_NODE( node ); } XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) { if (addThis->_document != _document) return 0; if (addThis->_parent) addThis->_parent->Unlink( addThis ); else addThis->_memPool->SetTracked(); 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 ) { if (addThis->_document != _document) return 0; if (addThis->_parent) addThis->_parent->Unlink( addThis ); else addThis->_memPool->SetTracked(); 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 ) { if (addThis->_document != _document) return 0; TIXMLASSERT( afterThis->_parent == this ); if ( afterThis->_parent != this ) { return 0; } if ( afterThis->_next == 0 ) { // The last node or the only node. return InsertEndChild( addThis ); } if (addThis->_parent) addThis->_parent->Unlink( addThis ); else addThis->_memPool->SetTracked(); 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* value ) const { for( XMLNode* node=_firstChild; node; node=node->_next ) { XMLElement* element = node->ToElement(); if ( element ) { if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { return element; } } } return 0; } const XMLElement* XMLNode::LastChildElement( const char* value ) const { for( XMLNode* node=_lastChild; node; node=node->_prev ) { XMLElement* element = node->ToElement(); if ( element ) { if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { return element; } } } return 0; } const XMLElement* XMLNode::NextSiblingElement( const char* value ) const { for( XMLNode* element=this->_next; element; element = element->_next ) { if ( element->ToElement() && (!value || XMLUtil::StringEqual( value, element->Value() ))) { return element->ToElement(); } } return 0; } const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const { for( XMLNode* element=_prev; element; element = element->_prev ) { if ( element->ToElement() && (!value || XMLUtil::StringEqual( value, element->Value() ))) { return element->ToElement(); } } return 0; } char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) { // 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 ); if ( p == 0 || node == 0 ) { break; } StrPair endTag; p = node->ParseDeep( p, &endTag ); if ( !p ) { DELETE_NODE( node ); node = 0; if ( !_document->Error() ) { _document->SetError( XML_ERROR_PARSING, 0, 0 ); } break; } // We read the end tag. Return it to the parent. if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) { if ( parentEnd ) { *parentEnd = static_cast(node)->_value; } node->_memPool->SetTracked(); // created and then immediately deleted. DELETE_NODE( node ); return p; } // Handle an end tag returned to this level. // And handle a bunch of annoying errors. XMLElement* ele = node->ToElement(); if ( ele ) { if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); p = 0; } else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); p = 0; } else if ( !endTag.Empty() ) { if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); p = 0; } } } if ( p == 0 ) { DELETE_NODE( node ); node = 0; } if ( node ) { this->InsertEndChild( node ); } } return 0; } // --------- XMLText ---------- // char* XMLText::ParseDeep( char* p, StrPair* ) { const char* start = p; if ( this->CData() ) { p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); } return p; } else { int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { flags |= StrPair::COLLAPSE_WHITESPACE; } p = _value.ParseText( p, "<", flags ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); } if ( p && *p ) { return p-1; } } 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 { return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() )); } bool XMLText::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLComment ---------- // XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) { } XMLComment::~XMLComment() { } char* XMLComment::ParseDeep( char* p, StrPair* ) { // Comment parses as text. const char* start = p; p = _value.ParseText( p, "-->", StrPair::COMMENT ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_COMMENT, start, 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 { return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() )); } bool XMLComment::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLDeclaration ---------- // XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) { } XMLDeclaration::~XMLDeclaration() { //printf( "~XMLDeclaration\n" ); } char* XMLDeclaration::ParseDeep( char* p, StrPair* ) { // Declaration parses as text. const char* start = p; p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 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 { return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() )); } bool XMLDeclaration::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLUnknown ---------- // XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) { } XMLUnknown::~XMLUnknown() { } char* XMLUnknown::ParseDeep( char* p, StrPair* ) { // Unknown parses as text. const char* start = p; p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 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 { return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() )); } bool XMLUnknown::Accept( XMLVisitor* visitor ) const { 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 ) { // 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 ); if ( !p || *p != '=' ) { return 0; } ++p; // move up to opening quote p = XMLUtil::SkipWhiteSpace( p ); 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 ); return p; } void XMLAttribute::SetName( const char* n ) { _name.SetStr( n ); } XMLError XMLAttribute::QueryIntValue( int* value ) const { if ( XMLUtil::ToInt( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const { if ( XMLUtil::ToUnsigned( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryBoolValue( bool* value ) const { if ( XMLUtil::ToBool( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryFloatValue( float* value ) const { if ( XMLUtil::ToFloat( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryDoubleValue( double* value ) const { if ( XMLUtil::ToDouble( Value(), value )) { return XML_NO_ERROR; } 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( 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( 0 ), _rootAttribute( 0 ) { } XMLElement::~XMLElement() { while( _rootAttribute ) { XMLAttribute* next = _rootAttribute->_next; DELETE_ATTRIBUTE( _rootAttribute ); _rootAttribute = next; } } XMLAttribute* XMLElement::FindAttribute( const char* name ) { XMLAttribute* a = 0; for( a=_rootAttribute; a; a = a->_next ) { if ( XMLUtil::StringEqual( a->Name(), name ) ) { return a; } } return 0; } const XMLAttribute* XMLElement::FindAttribute( const char* name ) const { XMLAttribute* a = 0; for( 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; } const char* XMLElement::GetText() const { if ( FirstChild() && FirstChild()->ToText() ) { return FirstChild()->ToText()->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( 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()->ToText()->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()->ToText()->Value(); if ( XMLUtil::ToUnsigned( t, uval ) ) { 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()->ToText()->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()->ToText()->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()->ToText()->Value(); if ( XMLUtil::ToFloat( t, fval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } 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 = new (_document->_attributePool.Alloc() ) XMLAttribute(); attrib->_memPool = &_document->_attributePool; if ( last ) { last->_next = attrib; } else { _rootAttribute = attrib; } attrib->SetName( name ); attrib->_memPool->SetTracked(); // always created and linked. } 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; } DELETE_ATTRIBUTE( a ); break; } prev = a; } } char* XMLElement::ParseAttributes( char* p ) { const char* start = p; XMLAttribute* prevAttribute = 0; // Read the attributes. while( p ) { p = XMLUtil::SkipWhiteSpace( p ); if ( !p || !(*p) ) { _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); return 0; } // attribute. if (XMLUtil::IsNameStartChar( *p ) ) { XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); attrib->_memPool = &_document->_attributePool; attrib->_memPool->SetTracked(); p = attrib->ParseDeep( p, _document->ProcessEntities() ); if ( !p || Attribute( attrib->Name() ) ) { DELETE_ATTRIBUTE( attrib ); _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); 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 ) { prevAttribute->_next = attrib; } else { _rootAttribute = attrib; } prevAttribute = attrib; } // end of the tag else if ( *p == '/' && *(p+1) == '>' ) { _closingType = CLOSED; return p+2; // done; sealed element. } // end of the tag else if ( *p == '>' ) { ++p; break; } else { _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); return 0; } } return p; } // // // foobar // char* XMLElement::ParseDeep( char* p, StrPair* strPair ) { // Read the element name. p = XMLUtil::SkipWhiteSpace( p ); if ( !p ) { return 0; } // 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 ); if ( !p || !*p || _closingType ) { return p; } p = XMLNode::ParseDeep( p, strPair ); 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 { const XMLElement* other = compare->ToElement(); if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { 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 { if ( visitor->VisitEnter( *this, _rootAttribute ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { break; } } } return visitor->VisitExit( *this ); } // --------- XMLDocument ----------- // XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : XMLNode( 0 ), _writeBOM( false ), _processEntities( processEntities ), _errorID( XML_NO_ERROR ), _whitespace( whitespace ), _errorStr1( 0 ), _errorStr2( 0 ), _charBuffer( 0 ) { _document = this; // avoid warning about 'this' in initializer list } XMLDocument::~XMLDocument() { DeleteChildren(); delete [] _charBuffer; #if 0 _textPool.Trace( "text" ); _elementPool.Trace( "element" ); _commentPool.Trace( "comment" ); _attributePool.Trace( "attribute" ); #endif #ifdef DEBUG if ( Error() == false ) { TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); } #endif } void XMLDocument::Clear() { DeleteChildren(); _errorID = XML_NO_ERROR; _errorStr1 = 0; _errorStr2 = 0; delete [] _charBuffer; _charBuffer = 0; } XMLElement* XMLDocument::NewElement( const char* name ) { XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); ele->_memPool = &_elementPool; ele->SetName( name ); return ele; } XMLComment* XMLDocument::NewComment( const char* str ) { XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); comment->_memPool = &_commentPool; comment->SetValue( str ); return comment; } XMLText* XMLDocument::NewText( const char* str ) { XMLText* text = new (_textPool.Alloc()) XMLText( this ); text->_memPool = &_textPool; text->SetValue( str ); return text; } XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) { XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); dec->_memPool = &_commentPool; dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); return dec; } XMLUnknown* XMLDocument::NewUnknown( const char* str ) { XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); unk->_memPool = &_commentPool; unk->SetValue( str ); return unk; } XMLError XMLDocument::LoadFile( const char* filename ) { Clear(); FILE* fp = 0; #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) errno_t err = fopen_s(&fp, filename, "rb" ); if ( !fp || err) { #else fp = fopen( filename, "rb" ); if ( !fp) { #endif SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); return _errorID; } LoadFile( fp ); fclose( fp ); return _errorID; } XMLError XMLDocument::LoadFile( FILE* fp ) { Clear(); fseek( fp, 0, SEEK_SET ); fgetc( fp ); if ( ferror( fp ) != 0 ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } fseek( fp, 0, SEEK_END ); size_t size = ftell( fp ); fseek( fp, 0, SEEK_SET ); if ( size == 0 ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } _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; const char* p = _charBuffer; p = XMLUtil::SkipWhiteSpace( p ); p = XMLUtil::ReadBOM( p, &_writeBOM ); if ( !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } ParseDeep( _charBuffer + (p-_charBuffer), 0 ); return _errorID; } XMLError XMLDocument::SaveFile( const char* filename, bool compact ) { FILE* fp = 0; #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) errno_t err = fopen_s(&fp, filename, "w" ); if ( !fp || err) { #else fp = fopen( filename, "w" ); if ( !fp) { #endif SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); return _errorID; } SaveFile(fp, compact); fclose( fp ); return _errorID; } XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) { XMLPrinter stream( fp, compact ); Print( &stream ); return _errorID; } XMLError XMLDocument::Parse( const char* p, size_t len ) { const char* start = p; Clear(); if ( len == 0 || !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } if ( len == (size_t)(-1) ) { len = strlen( p ); } _charBuffer = new char[ len+1 ]; memcpy( _charBuffer, p, len ); _charBuffer[len] = 0; p = XMLUtil::SkipWhiteSpace( p ); p = XMLUtil::ReadBOM( p, &_writeBOM ); if ( !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } ptrdiff_t delta = p - start; // skip initial whitespace, BOM, etc. ParseDeep( _charBuffer+delta, 0 ); return _errorID; } void XMLDocument::Print( XMLPrinter* streamer ) const { XMLPrinter stdStreamer( stdout ); if ( !streamer ) { streamer = &stdStreamer; } Accept( streamer ); } void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) { _errorID = error; _errorStr1 = str1; _errorStr2 = str2; } void XMLDocument::PrintError() const { if ( _errorID ) { static const int LEN = 20; char buf1[LEN] = { 0 }; char buf2[LEN] = { 0 }; if ( _errorStr1 ) { TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); } if ( _errorStr2 ) { TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); } printf( "XMLDocument error id=%d str1=%s str2=%s\n", _errorID, buf1, buf2 ); } } XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : _elementJustOpened( false ), _firstElement( true ), _fp( file ), _depth( depth ), _textDepth( -1 ), _processEntities( true ), _compactMode( compact ) { 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 { #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) int len = _vscprintf( format, va ); #else int len = vsnprintf( 0, 0, format, va ); #endif // Close out and re-start the va-args va_end( va ); va_start( va, format ); char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) vsnprintf_s( p, len+1, _TRUNCATE, format, va ); #else vsnprintf( p, len+1, format, va ); #endif } va_end( va ); } 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)(*q)] ) { while ( p < q ) { Print( "%c", *p ); ++p; } for( int i=0; i 0) ) { Print( "%s", p ); } } void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) { if ( writeBOM ) { static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; Print( "%s", bom ); } if ( writeDec ) { PushDeclaration( "xml version=\"1.0\"" ); } } void XMLPrinter::OpenElement( const char* name, bool compactMode ) { if ( _elementJustOpened ) { SealElement(); } _stack.Push( name ); if ( _textDepth < 0 && !_firstElement && !compactMode ) { Print( "\n" ); } if ( !compactMode ) { PrintSpace( _depth ); } Print( "<%s", name ); _elementJustOpened = true; _firstElement = false; ++_depth; } void XMLPrinter::PushAttribute( const char* name, const char* value ) { TIXMLASSERT( _elementJustOpened ); Print( " %s=\"", name ); PrintString( value, false ); Print( "\"" ); } 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, 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 ) { Print( "/>" ); } else { if ( _textDepth < 0 && !compactMode) { Print( "\n" ); PrintSpace( _depth ); } Print( "", name ); } if ( _textDepth == _depth ) { _textDepth = -1; } if ( _depth == 0 && !compactMode) { Print( "\n" ); } _elementJustOpened = false; } void XMLPrinter::SealElement() { _elementJustOpened = false; Print( ">" ); } void XMLPrinter::PushText( const char* text, bool cdata ) { _textDepth = _depth-1; if ( _elementJustOpened ) { SealElement(); } if ( cdata ) { Print( "" ); } else { PrintString( text, true ); } } 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 ) { if ( _elementJustOpened ) { SealElement(); } if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } _firstElement = false; Print( "", comment ); } void XMLPrinter::PushDeclaration( const char* value ) { if ( _elementJustOpened ) { SealElement(); } if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } _firstElement = false; Print( "", value ); } void XMLPrinter::PushUnknown( const char* value ) { if ( _elementJustOpened ) { SealElement(); } if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } _firstElement = false; Print( "", value ); } 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 = element.Parent()->ToElement(); 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.66/externals/tinyxml/tinyxml2.h000077500000000000000000001721711236713773000207230ustar00rootroot00000000000000/* 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__) # include # include # include # include # include # include #else # include # include # include # include # include # include #endif /* 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 ) || 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 #else # define TINYXML2_LIB #endif #if defined(DEBUG) # if defined(_MSC_VER) # define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() # 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 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) // Microsoft visual studio, version 2005 and higher. /*int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );*/ 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; } #define TIXML_SSCANF sscanf_s #else // GCC version 3 and higher //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf #endif /* Versioning, past 1.0.14: http://semver.org/ */ static const int TIXML2_MAJOR_VERSION = 2; static const int TIXML2_MINOR_VERSION = 1; static const int TIXML2_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, COLLAPSE_WHITESPACE = 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 ) { 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 ); char* ParseName( char* in ); private: void Reset(); void CollapseWhitespace(); enum { NEEDS_FLUSH = 0x100, NEEDS_DELETE = 0x200 }; // After parsing, if *_end != 0, it can be set to zero. int _flags; char* _start; char* _end; }; /* 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< T, INIT >() { _mem = _pool; _allocated = INIT; _size = 0; } ~DynArray() { if ( _mem != _pool ) { delete [] _mem; } } void Clear() { _size = 0; } void Push( T t ) { EnsureCapacity( _size+1 ); _mem[_size++] = t; } T* PushArr( int count ) { EnsureCapacity( _size+count ); T* ret = &_mem[_size]; _size += count; return ret; } T Pop() { 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 { return _size; } int Capacity() const { return _allocated; } const T* Mem() const { return _mem; } T* Mem() { return _mem; } private: void EnsureCapacity( int cap ) { if ( cap > _allocated ) { int newAllocated = cap * 2; T* newMem = new T[newAllocated]; 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[INIT]; 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; }; /* Template child class to create pools of the correct type. */ template< int SIZE > class MemPoolT : public MemPool { public: MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} ~MemPoolT() { // Delete the blocks. for( int i=0; i<_blockPtrs.Size(); ++i ) { delete _blockPtrs[i]; } } virtual int ItemSize() const { return SIZE; } int CurrentAllocs() const { return _currentAllocs; } virtual void* Alloc() { if ( !_root ) { // Need a new block. Block* block = new Block(); _blockPtrs.Push( block ); for( int i=0; ichunk[i].next = &block->chunk[i+1]; } block->chunk[COUNT-1].next = 0; _root = block->chunk; } void* result = _root; _root = _root->next; ++_currentAllocs; if ( _currentAllocs > _maxAllocs ) { _maxAllocs = _currentAllocs; } _nAllocs++; _nUntracked++; return result; } virtual void Free( void* mem ) { if ( !mem ) { return; } --_currentAllocs; Chunk* chunk = (Chunk*)mem; #ifdef DEBUG memset( chunk, 0xfe, sizeof(Chunk) ); #endif chunk->next = _root; _root = chunk; } void Trace( const char* name ) { printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, 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 enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private private: union Chunk { Chunk* next; char mem[SIZE]; }; struct Block { Chunk chunk[COUNT]; }; DynArray< Block*, 10 > _blockPtrs; Chunk* _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; } }; /* Utility functionality. */ class XMLUtil { public: // 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 const char* SkipWhiteSpace( const char* p ) { while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { ++p; } return p; } static char* SkipWhiteSpace( char* p ) { while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { ++p; } return p; } static bool IsWhiteSpace( char p ) { return !IsUTF8Continuation(p) && isspace( static_cast(p) ); } inline static bool IsNameStartChar( unsigned char ch ) { return ( ( ch < 128 ) ? isalpha( ch ) : 1 ) || 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 ) { int n = 0; if ( p == q ) { return true; } while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); } /// Get the last child node, or null if none exists. const XMLNode* LastChild() const { return _lastChild; } XMLNode* LastChild() { return const_cast(const_cast(this)->LastChild() ); } /** Get the last child element or optionally the last child element with the specified name. */ const XMLElement* LastChildElement( const char* value=0 ) const; XMLElement* LastChildElement( const char* value=0 ) { return const_cast(const_cast(this)->LastChildElement(value) ); } /// 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* value=0 ) const ; XMLElement* PreviousSiblingElement( const char* value=0 ) { return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); } /// 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* value=0 ) const; XMLElement* NextSiblingElement( const char* value=0 ) { return const_cast(const_cast(this)->NextSiblingElement( value ) ); } /** 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; /** 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; // internal virtual char* ParseDeep( char*, StrPair* ); protected: XMLNode( XMLDocument* ); virtual ~XMLNode(); XMLNode( const XMLNode& ); // not supported XMLNode& operator=( const XMLNode& ); // not supported XMLDocument* _document; XMLNode* _parent; mutable StrPair _value; XMLNode* _firstChild; XMLNode* _lastChild; XMLNode* _prev; XMLNode* _next; private: MemPool* _memPool; void Unlink( XMLNode* child ); }; /** 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 XMLBase; 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; } char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} XMLText( const XMLText& ); // not supported XMLText& operator=( const XMLText& ); // not supported private: bool _isCData; }; /** 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; char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLComment( XMLDocument* doc ); virtual ~XMLComment(); XMLComment( const XMLComment& ); // not supported XMLComment& operator=( const XMLComment& ); // not supported private: }; /** 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; char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); 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; char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); XMLUnknown( const XMLUnknown& ); // not supported XMLUnknown& operator=( const XMLUnknown& ); // not supported }; enum XMLError { XML_NO_ERROR = 0, 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, XML_ERROR_ELEMENT_MISMATCH, XML_ERROR_PARSING_ELEMENT, XML_ERROR_PARSING_ATTRIBUTE, 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 }; /** 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; /// 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; } /// 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_NO_ERROR 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 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( 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() : _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 ); mutable StrPair _name; mutable StrPair _value; 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 XMLBase; 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. 0 will be returned if there is an error. For a method with error checking, see QueryIntAttribute() */ int IntAttribute( const char* name ) const { int i=0; QueryIntAttribute( name, &i ); return i; } /// See IntAttribute() unsigned UnsignedAttribute( const char* name ) const { unsigned i=0; QueryUnsignedAttribute( name, &i ); return i; } /// See IntAttribute() bool BoolAttribute( const char* name ) const { bool b=false; QueryBoolAttribute( name, &b ); return b; } /// See IntAttribute() double DoubleAttribute( const char* name ) const { double d=0; QueryDoubleAttribute( name, &d ); return d; } /// See IntAttribute() float FloatAttribute( const char* name ) const { float f=0; QueryFloatAttribute( name, &f ); return f; } /** Given an attribute name, QueryIntAttribute() returns XML_NO_ERROR, 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 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 ); } /** Given an attribute name, QueryAttribute() returns XML_NO_ERROR, 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, 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, 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 and element. See SetText() for important limitations. void SetText( int value ); /// Convenience method for setting text inside and element. See SetText() for important limitations. void SetText( unsigned value ); /// Convenience method for setting text inside and element. See SetText() for important limitations. void SetText( bool value ); /// Convenience method for setting text inside and element. See SetText() for important limitations. void SetText( double value ); /// Convenience method for setting text inside and 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 QueryBoolText( bool* bval ) const; /// See QueryIntText() XMLError QueryDoubleText( double* dval ) const; /// See QueryIntText() XMLError QueryFloatText( float* fval ) const; // internal: enum { OPEN, // CLOSED, // CLOSING // }; int ClosingType() const { return _closingType; } char* ParseDeep( char* p, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; private: XMLElement( XMLDocument* doc ); virtual ~XMLElement(); XMLElement( const XMLElement& ); // not supported void operator=( const XMLElement& ); // not supported XMLAttribute* FindAttribute( const char* name ); XMLAttribute* FindOrCreateAttribute( const char* name ); //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p ); enum { BUF_SIZE = 200 }; int _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; public: /// constructor XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); ~XMLDocument(); virtual XMLDocument* ToDocument() { return this; } virtual const XMLDocument* ToDocument() const { return this; } /** Parse an XML file from a character string. Returns XML_NO_ERROR (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_NO_ERROR (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*. Returns XML_NO_ERROR (0) on success, or an errorID. */ XMLError LoadFile( FILE* ); /** Save the XML file to disk. Returns XML_NO_ERROR (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_NO_ERROR (0) on success, or an errorID. */ XMLError SaveFile( FILE* fp, bool compact = false ); bool ProcessEntities() const { return _processEntities; } Whitespace WhitespaceMode() const { return _whitespace; } /** 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 ) { node->_parent->DeleteChild( node ); } void SetError( XMLError error, const char* str1, const char* str2 ); /// Return true if there was an error parsing the document. bool Error() const { return _errorID != XML_NO_ERROR; } /// Return the errorID. XMLError ErrorID() const { return _errorID; } /// Return a possibly helpful diagnostic location or string. const char* GetErrorStr1() const { return _errorStr1; } /// Return a possibly helpful secondary diagnostic location or string. const char* GetErrorStr2() const { return _errorStr2; } /// If there is an error, print it to stdout. void PrintError() const; /// Clear the document, resetting it to the initial state. void Clear(); // internal char* Identify( char* p, XMLNode** node ); 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 _whitespace; const char* _errorStr1; const char* _errorStr2; char* _charBuffer; MemPoolT< sizeof(XMLElement) > _elementPool; MemPoolT< sizeof(XMLAttribute) > _attributePool; MemPoolT< sizeof(XMLText) > _textPool; MemPoolT< sizeof(XMLComment) > _commentPool; }; /** 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.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); 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* value=0 ) { return XMLHandle( _node ? _node->FirstChildElement( value ) : 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* _value=0 ) { return XMLHandle( _node ? _node->LastChildElement( _value ) : 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* _value=0 ) { return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 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* _value=0 ) { return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 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() ) ? _node->ToElement() : 0 ); } /// Safe cast to XMLText. This can return null. XMLText* ToText() { return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); } /// Safe cast to XMLUnknown. This can return null. XMLUnknown* ToUnknown() { return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); } /// Safe cast to XMLDeclaration. This can return null. XMLDeclaration* ToDeclaration() { return ( ( _node && _node->ToDeclaration() ) ? _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* value=0 ) const { return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); } const XMLConstHandle LastChild() const { return XMLConstHandle( _node ? _node->LastChild() : 0 ); } const XMLConstHandle LastChildElement( const char* _value=0 ) const { return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); } const XMLConstHandle PreviousSibling() const { return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); } const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); } const XMLConstHandle NextSibling() const { return XMLConstHandle( _node ? _node->NextSibling() : 0 ); } const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); } const XMLNode* ToNode() const { return _node; } const XMLElement* ToElement() const { return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); } const XMLText* ToText() const { return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); } const XMLUnknown* ToUnknown() const { return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); } const XMLDeclaration* ToDeclaration() const { return ( ( _node && _node->ToDeclaration() ) ? _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, 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 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); } 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 SealElement(); 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; #ifdef _MSC_VER DynArray< char, 20 > _accumulator; #endif }; } // tinyxml2 #if defined(_MSC_VER) # pragma warning(pop) #endif #endif // TINYXML2_INCLUDED cppcheck-1.66/generate_coverage_report000077500000000000000000000007341236713773000202310ustar00rootroot00000000000000#!/bin/bash set -e make clean rm -rf coverage_report make test CXXFLAGS="-g -fprofile-arcs -ftest-coverage" 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 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.66/gui/000077500000000000000000000000001236713773000140235ustar00rootroot00000000000000cppcheck-1.66/gui/about.ui000066400000000000000000000102071236713773000154740ustar00rootroot00000000000000 About 0 0 478 300 About Cppcheck :/icon.png Qt::Vertical 20 40 Qt::Horizontal 40 20 Version %1 Cppcheck - A tool for static C/C++ code analysis. true Copyright © 2007-2014 Daniel Marjamäki and 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.66/gui/aboutdialog.cpp000066400000000000000000000026151236713773000170250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.sourceforge.net/"; mUI.mHomepage->setText(mUI.mHomepage->text().arg(url)); connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); } cppcheck-1.66/gui/aboutdialog.h000066400000000000000000000022411236713773000164650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/application.cpp000066400000000000000000000017241236713773000170360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/application.h000066400000000000000000000057241236713773000165070ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/application.ui000066400000000000000000000125211236713773000166660ustar00rootroot00000000000000 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.66/gui/applicationdialog.cpp000066400000000000000000000057321236713773000202210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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, SIGNAL(clicked()), this, SLOT(Browse())); connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(Ok())); connect(mUI.mButtons, SIGNAL(rejected()), this, SLOT(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_WS_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_WS_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.66/gui/applicationdialog.h000066400000000000000000000035161236713773000176640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/applicationlist.cpp000066400000000000000000000145741236713773000177410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "common.h" #include "applicationlist.h" #include "application.h" 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 << ""; 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; } // 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::FindDefaultWindowsEditor() { bool foundOne = false; const QString appPath(getenv("ProgramFiles")); const QString notepadppPath = appPath + "\\Notepad++\\notepad++.exe"; if (QFileInfo(notepadppPath).exists() && QFileInfo(notepadppPath).isExecutable()) { Application app; app.setName("Notepad++"); app.setPath("\"" + notepadppPath + "\""); app.setParameters("-n(line) (file)"); AddApplication(app); foundOne = true; } const QString notepadTwoPath = appPath + "\\Notepad2\\Notepad2.exe"; if (QFileInfo(notepadTwoPath).exists() && QFileInfo(notepadTwoPath).isExecutable()) { Application app; app.setName("Notepad2"); app.setPath("\"" + notepadTwoPath + "\""); app.setParameters("/g (line) (file)"); AddApplication(app); foundOne = true; } const QString windowsPath(getenv("windir")); const QString notepadPath = windowsPath + "\\system32\\notepad.exe"; if (QFileInfo(notepadPath).exists() && QFileInfo(notepadPath).isExecutable()) { Application app; app.setName("Notepad"); app.setPath(notepadPath); app.setParameters("(file)"); AddApplication(app); foundOne = true; } return foundOne; } cppcheck-1.66/gui/applicationlist.h000066400000000000000000000063131236713773000173760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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: /** * @brief List of applications * */ QList mApplications; /** * @brief Index of the default application. * */ int mDefaultApplicationIndex; }; /// @} #endif // APPLICATIONLIST_H cppcheck-1.66/gui/checkstatistics.cpp000066400000000000000000000043711236713773000177240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkstatistics.h" CheckStatistics::CheckStatistics(QObject *parent) : QObject(parent) { Clear(); } void CheckStatistics::AddItem(ShowTypes::ShowType type) { switch (type) { case ShowTypes::ShowStyle: mStyle++; break; case ShowTypes::ShowWarnings: mWarning++; break; case ShowTypes::ShowPerformance: mPerformance++; break; case ShowTypes::ShowPortability: mPortability++; break; case ShowTypes::ShowErrors: mError++; break; case ShowTypes::ShowInformation: mInformation++; break; case ShowTypes::ShowNone: default: qDebug() << "Unknown error type - not added to statistics."; break; } } void CheckStatistics::Clear() { mStyle = 0; mWarning = 0; mPerformance = 0; mPortability = 0; mInformation = 0; mError = 0; } unsigned CheckStatistics::GetCount(ShowTypes::ShowType type) const { switch (type) { case ShowTypes::ShowStyle: return mStyle; case ShowTypes::ShowWarnings: return mWarning; case ShowTypes::ShowPerformance: return mPerformance; case ShowTypes::ShowPortability: return mPortability; case ShowTypes::ShowErrors: return mError; case ShowTypes::ShowInformation: return mInformation; case ShowTypes::ShowNone: default: qDebug() << "Unknown error type - returning zero statistics."; return 0; } } cppcheck-1.66/gui/checkstatistics.h000066400000000000000000000032341236713773000173660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: CheckStatistics(QObject *parent = NULL); /** * @brief Add new checked item to statistics. * * @param type Type of the item to add. */ void AddItem(ShowTypes::ShowType type); /** * @brief Clear the statistics. * */ void Clear(); /** * @brief Return statistics for given type. * * @param type Type for which the statistics are returned. * @return Number of items of given type. */ unsigned GetCount(ShowTypes::ShowType type) const; private: unsigned mStyle; unsigned mWarning; unsigned mPerformance; unsigned mPortability; unsigned mInformation; unsigned mError; }; /// @} #endif // CHECKSTATISTICS_H cppcheck-1.66/gui/checkthread.cpp000066400000000000000000000032521236713773000167760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkthread.h" #include "threadresult.h" #include "cppcheck.h" CheckThread::CheckThread(ThreadResult &result) : mState(Ready), mResult(result), mCppcheck(result, true) { //ctor } CheckThread::~CheckThread() { //dtor } void CheckThread::Check(const Settings &settings) { mCppcheck.settings() = settings; start(); } void CheckThread::run() { mState = Running; QString file; file = mResult.GetNextFile(); while (!file.isEmpty() && mState == Running) { qDebug() << "Checking file" << file; mCppcheck.check(file.toStdString()); emit FileChecked(file); if (mState == Running) file = mResult.GetNextFile(); } if (mState == Running) mState = Ready; else mState = Stopped; emit Done(); } void CheckThread::stop() { mState = Stopping; mCppcheck.terminate(); } cppcheck-1.66/gui/checkthread.h000066400000000000000000000043641236713773000164500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: CheckThread(ThreadResult &result); virtual ~CheckThread(); /** * @brief Set settings for cppcheck * * @param settings settings for cppcheck */ void Check(const Settings &settings); /** * @brief method that is run in a thread * */ void run(); void stop(); 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: }; /// @} #endif // CHECKTHREAD_H cppcheck-1.66/gui/common.cpp000066400000000000000000000026571236713773000160310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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, "").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, "").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.66/gui/common.h000066400000000000000000000110131236713773000154600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 /// @{ /** * 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_C89 "Platform C89" #define SETTINGS_STD_C99 "Platform C99" #define SETTINGS_STD_C11 "Platform C11" #define SETTINGS_STD_POSIX "Platform Posix" // 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_INLINE_SUPPRESSIONS "Inline suppressions" #define SETTINGS_INCONCLUSIVE_ERRORS "Inconclusive errors" #define SETTINGS_MRU_PROJECTS "MRU Projects" #define SETTINGS_SHOW_ERROR_ID "Show error Id" // 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.66/gui/cppcheck-gui.rc000066400000000000000000000002661236713773000167170ustar00rootroot00000000000000// Include command line program's resource file containing version info #include "../cli/version.rc" // GUI Icon IDI_ICON1 ICON DISCARDABLE "cppcheck.ico" cppcheck-1.66/gui/cppcheck.ico000066400000000000000000000611761236713773000163120ustar00rootroot00000000000000 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.66/gui/cppcheck_de.ts000066400000000000000000001760401236713773000166330ustar00rootroot00000000000000 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-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2014 Daniel Marjamäki und das 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 LogView Checking Log Untersuchungs-Log Clear Löschen Save Log Speichere Log Text files (*.txt *.log);;All files (*.*) Textdateien (*.txt *.log);;Alle Dateien(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Datei kann nicht zum Schreiben geöffnet werden: "%1" MainWindow Cppcheck Cppcheck Standard Standard &File &Datei &View &Ansicht &Toolbars &Symbolleisten &Check &Prüfen C++ standard C++-Standard C standard C-Standard &Edit &Bearbeiten &License... &Lizenz... A&uthors... &Autoren... &About... Ü&ber... &Files... &Dateien... Check files Prüfe Dateien Ctrl+F Strg+F &Directory... &Verzeichnis... Check directory Prüfe Verzeichnis Ctrl+D Strg+D &Recheck files Dateien &neu prüfen Ctrl+R Strg+R &Stop &Stoppen Stop checking Prüfung abbrechen Esc Esc &Save results to file... &Ergebnisse in Datei speichern... Ctrl+S Strg+S &Quit &Beenden &Clear results Ergebnisse &löschen &Preferences &Einstellungen Errors Fehler Show errors Zeige Fehler Show S&cratchpad... Zeige Schmierzettel Warnings Warnungen Show warnings Zeige Warnungen Performance warnings Performance-Warnungen Show performance warnings Zeige Performance-Warnungen Show &hidden Zeige &versteckte Information Information Show information messages Zeige Informationsmeldungen Portability Portabilität Show portability warnings Zeige Portabilitätswarnungen &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 Platforms Plattformen C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Check all Alle &auswählen Filter Filter &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... &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 &Contents &Inhalte Categories Kategorien Style warnings Stilwarnungen Show style warnings Zeige Stilwarnungen Open the help contents Öffnet die Hilfe-Inhalte F1 F1 &Help &Hilfe Select directory to check Verzeichnis zum Überprüfen auswählen No suitable files found to check! Keine passenden Dateien zum Überprüfen gefunden! Quick Filter: Schnellfilter: Found project file: %1 Do you want to load this project file instead? Gefundene Projektdatei: %1 Möchten Sie stattdessen diese öffnen? Found project files from the directory. Do you want to proceed checking without using any of these project files? Projektdatei im Verzeichnis gefunden. Möchten Sie die Prüfung wirklich durchführen, ohne eine Projektdatei zu verwenden? File not found Datei nicht gefunden Bad XML Fehlerhaftes XML Unexpected element Unerwartetes Element Missing attribute Fehlendes Attribut Bad attribute Fehlerhaftes Attribut Bad attribute value Falscher Attributwert Failed to load the selected library '%1'. %2 Laden der ausgewählten Bibliothek '%1' schlug fehl. %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. 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. License Lizenz Authors Autoren XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML-Dateien Version 2 (*.xml);;XML-Dateien Version 1 (*.xml);;Textdateien (*.txt);;CSV-Dateien (*.csv) 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! Select files to check Dateien zum Überprüfen auswählen Error Fehler 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 Checking is running. Do you want to stop the checking and exit Cppcheck?. Prüfung läuft. Möchten Sie die Prüfung abbrechen und Cppcheck beenden? XML files version 1 (*.xml) XML-Dateien Version 1 (*.xml) XML files version 2 (*.xml) XML-Dateien Version 2 (*.xml) 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: 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? Finnish Finnisch English Englisch Chinese (Simplified) Chinesisch (vereinfacht) Dutch Niederländisch French Französisch Italian Italienisch Korean Koreanisch Spanish Spanisch Swedish Schwedisch German Deutsch Russian Russisch Japanese Japanisch Serbian Serbisch 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 [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 Cppcheck GUI - Command line parameters Cppcheck GUI - Kommandozeilenparameter Platforms Built-in Built-In 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. Projektdatei konnte nicht gelesen werden. Could not write the project file. Projektdatei konnte nicht geschrieben werden. ProjectFile Project File Projektdatei Project Projekt Root: Wurzel: Libraries: Bibliotheken: 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. Paths: Pfade: Add... Hinzufügen... Edit Bearbeiten Remove Entfernen Includes Includes Include directories: Include-Verzeichnisse: Up Auf Down Ab Exclude Ausschließen Suppressions Fehlerunterdrückungen Suppression list: Fehlerunterdrückungsliste: Add Hinzufügen Defines: Definitionen: ProjectFileDialog Project file: %1 Projektdatei: %1 Select include directory Wähle Include-Verzeichnisse Select a directory to check Wähle zu prüfendes Verzeichnis 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 ResultsTree File Datei Severity Schweregrad Line Zeile Summary Zusammenfassung Undefined file Undefinierte Datei [Inconclusive] [unklar] debug Debug 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 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 style Stil error Fehler warning Warnung performance Performance portability Portabilität information Information ResultsView %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. Summary Zusammenfassung Message Meldung Id Id 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 ScratchPad Scratchpad Schmierzettel filename Dateiname Check Prüfe Settings Preferences Einstellungen General Allgemein Include paths: Include-Pfade: 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 Paths Pfade Edit Bearbeiten 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 Advanced Erweitert &Show inconclusive errors &Unklare Fehler anzeigen S&how internal warnings in log &Interne Warnungen im Log anzeigen SettingsDialog N/A kA Add a new application Neue Anwendung hinzufügen Modify an application Anwendung ändern [Default] [Standard] Select include directory Wähle Include-Verzeichnis 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: Copy to Clipboard In die Zwischenablage kopieren 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 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 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.66/gui/cppcheck_es.ts000066400000000000000000001751231236713773000166530ustar00rootroot00000000000000 About About Cppcheck Sobre Cppcheck Version %1 Versión %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Utilidad para análisis estático de código C/C++. Copyright © 2007-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright © 2007-2010 Daniel Marjamäki y 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 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: &Executable: &Parameters: Browse Buscar Executable files (*.exe);;All files(*.*) Ficheros ejecutables (*.exe);;Todos los ficheros(*.*) Select viewer application Selecciona la aplicación para visualizar Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! 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 LogView Checking Log Comprobando log Clear Limpiar Save Log Guardar Log Text files (*.txt *.log);;All files (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" No se pudo abrir el fichero para escritura: "%1" MainWindow Cppcheck Cppcheck &File &Fichero &View &Ver &Toolbars &Herramientas &Help &Ayuda &Check C++ standard C standard &Edit &Editar Standard Estándar Categories Categorías &License... &Licencia... A&uthors... A&utores... &About... &Sobre... &Files... &Ficheros... Check files Checkear ficheros Ctrl+F Ctrl+F &Directory... &Carpeta... Check directory Chequear carpeta Ctrl+D Ctrl+D &Recheck files &Volver a revisar ficheros Ctrl+R Ctrl+R &Stop &Detener Stop checking Detener chequeo 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 &Filter &Filtro Filter results Resultados de filtro Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Platforms C++11 C99 Posix C11 C89 C++03 &Check all &Seleccionar todo Filter &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... &New Project File... &Nuevo Proyecto... &Log View &Vista de log Log View Vista de log C&lose Project File C&errar Proyecto &Edit Project File... &Editar Proyecto... &Statistics &Estadísticas Warnings Advertencias Show warnings Mostrar advertencias Performance warnings Advertencias de rendimiento Show performance warnings Mostrar advertencias de rendimiento Show &hidden Mostrar &ocultas 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 fichero de proyecto antes de seleccionar nuevos ficheros o carpetas! Select directory to check Selecciona una carpeta para comprobar File not found Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. XML files (*.xml) Ficheros 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) Save the report file Guardar informe Quick Filter: Filtro rápido: Select files to check Selecciona ficheros para comprobar Found project file: %1 Do you want to load this project file instead? Se encontró el fichero de proyecto: %1 Quiere cargar dicho fichero de proyecto? 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 chequear sin utilizar alguno de estos ficheros de proyecto? Error 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) XML files version 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 proyecto Project: Proyecto: 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? English English Dutch Holandés Chinese (Simplified) Finnish Finés French Italian Korean Spanish Swedish Sueco German Alemán Russian Ruso Japanese Japonés Serbian Servio 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 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. 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 Fichero de proyecto Project Proyecto Root: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Rutas: Add... Añadir... Edit Editar Remove Eliminar Includes Include directories: Up Down Exclude Suppressions Suppression list: Add Defines: Definiciones: ProjectFileDialog Project file: %1 Fichero de proyecto: %1 Select include directory Selecciona una carpeta para incluir Select a directory to check Select directory to ignore Add Suppression Select error id suppress: QDialogButtonBox OK Cancel Close Cerrar Save Guardar QObject Unknown language specified! 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 ResultsTree File Fichero Severity Severidad Line Línea Summary Resumen Undefined file Fichero no definido [Inconclusive] portability information debug Copy filename Copia nombre Copy full path Copia ruta Copy message Copia mensaje Copy message id Hide Oculta Hide all with id 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. Configura el programa para ver ficheros en Cppcheck preferencias/Aplicaciones. No default editor application selected. Please select the default editor application in preferences/Applications. 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 style estilo error error warning advertencia performance ajuste ResultsView Results Resultados No errors found, nothing to save. No se han encontrado errores, nada que guardar. Failed to save the report. Error al guardar el informe. %p% (%1 of %2 files checked) %p% (%1 de %2 ficheros chequeados) 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, abrir el menú vista. Failed to read the report. Error al leer el informe. Summary Resumen Message Mensaje Id ScratchPad Scratchpad filename Nombre de fichero Check Settings Preferences Preferencias General General Ideal count: Cantidad ideal: Force checking all #ifdef configurations Forzar chequeo de todas las configuraciones #ifdef Display error Id in column "Id" Mostrar Id de error en la columna "Id" Enable inline suppressions Habilitar supresiones inline Paths Include paths: Incluye rutas: Add... Añadir... Edit... Editar... Set as default Definir como default Language Idioma Advanced Avanzado &Show inconclusive errors Mostrar errores "inconclusive" S&how internal warnings in log Mostrar warnings internas en 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 Add a new application Añade una nueva aplicación Modify an application Modificar aplicación [Default] Select include directory Seleccionar carpeta incluida StatsDialog Statistics Estadísticas Project Proyecto Project: Proyecto: Paths: Rutas: Include paths: Incluye rutas: Defines: Definiciones: Previous Scan Revisión anterior Path Selected: Ruta seleccionada: Number of Files Scanned: Número de ficheros revisados: Scan Duration: Tiempo de revisión: 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: Copy to Clipboard Copiar al portapapeles 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Project Settings Paths Include paths Defines Path selected Number of files scanned Scan duration Errors Errores Warnings Advertencias Style warnings Advertencias de estilo Portability warnings Performance warnings Ajustar advertencias Information messages Mensajes de información ThreadResult %1 of %2 files checked %1 de %2 ficheros chequeados 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. Hubo un fallo al cambiar el idioma de la interfaz gráfica: %1 El idioma de la interfaz grafica ha sido cambiado a Inglés. Abra la ventana de Preferencias para seleccionar alguno de los idiomas disponibles. Cppcheck Cppcheck TxtReport inconclusive cppcheck-1.66/gui/cppcheck_fi.ts000066400000000000000000001725531236713773000166460ustar00rootroot00000000000000 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-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright (C) 2007-2009 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 LogView Checking Log Clear Save Log Text files (*.txt *.log);;All files (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" MainWindow Cppcheck Cppcheck Standard Vakio &File &Tiedosto &View &Näytä &Toolbars &Check &Tarkista C++ standard C standard &Edit &Muokkaa &License... &Lisenssi... A&uthors... &Tekijät... &About... &Tietoa ohjelmasta Cppcheck... &Files... &Tiedostot... Check files Ctrl+F Ctrl+F &Directory... &Hakemisto... Check directory Ctrl+D Ctrl+D &Recheck files Tarkista tiedostot &uudelleen Ctrl+R Ctrl+R &Stop &Pysäytä Stop checking Esc Esc &Save results to file... &Tallenna tulokset tiedostoon... Ctrl+S Ctrl+S &Quit &Lopeta &Clear results &Tyhjennä tulokset &Preferences &Asetukset Errors Show errors Show S&cratchpad... Warnings Show warnings Performance warnings Show performance warnings Show &hidden Information Show information messages Portability Show portability warnings &Filter Filter results Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Platforms C++11 C99 Posix C11 C89 C++03 &Check all &Valitse kaikki Filter &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... &New Project File... &Log View Log View C&lose Project File &Edit Project File... &Statistics &Contents Categories Style warnings 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: Found project file: %1 Do you want to load this project file instead? Found project files from the directory. Do you want to proceed checking without using any of these project files? File not found Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. 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 Error Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Open the report file Checking is running. Do you want to stop the checking and exit Cppcheck?. XML files version 1 (*.xml) XML files version 2 (*.xml) Text files (*.txt) Tekstitiedostot (*.txt) CSV files (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) 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? Finnish Suomi English Englanti Chinese (Simplified) Dutch French Italian Korean Spanish Swedish Ruotsi German Saksa Russian Venäjä Japanese Japanease Serbian 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 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 Root: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Add... Edit Remove Includes Include directories: Up Down Exclude Suppressions Suppression list: Add Defines: ProjectFileDialog Project file: %1 Select include directory Select a directory to check 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 ResultsTree File Tiedosto Severity Tyyppi Line Rivi Summary Undefined file Määrittelemätön tiedosto [Inconclusive] debug Copy filename Kopioi tiedostonimi Copy full path Kopioi tiedoston koko polku Copy message Copy message id Hide Hide all with id 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 style Tyyli error Yleinen warning performance portability information ResultsView %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. Summary Message Id 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 ScratchPad Scratchpad filename Check Settings Preferences Asetukset General Yleiset Include paths: 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 Paths Edit 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 Advanced &Show inconclusive errors S&how internal warnings in log SettingsDialog N/A Add a new application Lisää uusi ohjelma Modify an application Muokkaa ohjelmaa [Default] 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: Copy to Clipboard 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Project Settings Paths Include paths Defines Path selected Number of files scanned Scan duration Errors 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.66/gui/cppcheck_fr.ts000066400000000000000000001255731236713773000166570ustar00rootroot00000000000000 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 license GNU General Public License version 3 Visit Cppcheck homepage at %1 Visitez le site Cppcheck : %1 Copyright © 2007-2014 Daniel Marjamäki and 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 LogView Checking Log Clear Save Log Text files (*.txt *.log);;All files (*.*) Cppcheck Could not open file for writing: "%1" MainWindow Cppcheck &File &Fichier &View &Affichage &Help &Aide &Check &Vérifier &Edit &Édition Standard Standard &License... &License... A&uthors... A&uteur... &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 &afficher &Contents &Contenu Open the help contents Ouvir l'aide F1 No suitable files found to check! Pas de fichiers trouvés ! Select directory to check Sélectionner le répertoire à vérifier License Licence Authors Auteur Save the report file Sauvegarder le rapport XML files (*.xml) Fichier XML (*.xml) Text files (*.txt) Fichier Texte (*.txt) CSV files (*.csv) Fichier CSV (*.csv) Cppcheck - %1 English Anglais Dutch Hollandais Finnish Finlandais Swedish Suédois German Allemand Russian Russe &Toolbars Categories Check files Check directory Stop checking Style warnings Show style warnings Errors Show errors &Standard Standard items Toolbar &Categories Error categories &Open XML... Open P&roject File... &New Project File... &Log View Log View C&lose Project File &Edit Project File... &Statistics Warnings Show warnings Performance warnings Show performance warnings Show &hidden Information Show information messages Portability Show portability warnings You must close the project file before selecting new files or directories! Open the report file Checking is running. Do you want to stop the checking and exit Cppcheck?. XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files version 1 (*.xml) XML files version 2 (*.xml) Project files (*.cppcheck);;All files(*.*) Select Project File Select Project Filename No project file loaded Japanese Serbian French 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. Spanish &Filter Filter results Quick Filter: Found project file: %1 Do you want to load this project file instead? Found project files from the directory. Do you want to proceed checking without using any of these project files? Project: The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Filter Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Platforms C++11 C99 Posix Korean Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Italian Show S&cratchpad... Chinese (Simplified) Select files to check Sélectionner les fichiers à vérifier Cppcheck GUI - Command line parameters C++ standard C standard C11 C89 C++03 Error 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) 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. File not found Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%1'. %2 Platforms Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Built-in Project Cppcheck Could not read the project file. Could not write the project file. ProjectFile Project File Paths: Defines: Project Add... Edit Remove Includes Include directories: Root: Up Down Exclude Libraries: Suppressions Suppression list: Add Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. ProjectFileDialog Project file: %1 Select include directory Select directory to ignore Select a directory to check Add Suppression Select error id suppress: QDialogButtonBox OK Cancel Close Save 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! ResultsTree File Fichier Severity Sévérité Line Ligne Undefined file Fichier indéterminé Copy filename Copier 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 Hide Could not find the file! Could not find file: %1 Please select the directory where file is located. Select Directory warning performance portability information debug [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 Copy message id Hide all with id 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. Summary Message Message %p% (%1 of %2 files checked) Id ScratchPad Scratchpad filename Check Settings Preferences Préférences General Général Number of threads: Nombre de processus : 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: Add... Ideal count: Force checking all #ifdef configurations Enable inline suppressions Language Paths Edit Remove Edit... Set as default Advanced &Show inconclusive errors S&how internal warnings in log Display error Id in column "Id" SettingsDialog Add a new application Ajouter une nouvelle application Modify an application Modifier une application N/A Select include directory [Default] 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: Copy to Clipboard 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Project Settings Paths Include paths Defines Path selected Number of files scanned Scan duration Errors 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 TxtReport inconclusive cppcheck-1.66/gui/cppcheck_it.ts000066400000000000000000001763331236713773000166640ustar00rootroot00000000000000 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-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright (C) 2007-2012 Daniel Marjamäki ed 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 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 Standard Standard &File &File &View &Visualizza &Toolbars &Barre degli strumenti &Check &Scansiona C++ standard C standard &Edit &Modifica &License... &Licenza... A&uthors... A&utori... &About... I&nformazioni su... &Files... &File... Check files Scansiona i file Ctrl+F Ctrl+F &Directory... &Cartella... Check directory Scansiona la cartella Ctrl+D Ctrl+D &Recheck files &Riscansiona i file Ctrl+R Ctrl+R &Stop &Ferma 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à &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 &Check all &Seleziona tutto Filter Filtro &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... &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 &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: 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 Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. 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 Error 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: 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? Finnish Finnish English English Chinese (Simplified) Dutch Dutch French French Italian Italian Korean Korean Spanish Spanish Swedish Swedish German German Russian Russian Japanese Japanese Serbian Serbian 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 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 Root: Root: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Percorsi: Add... Aggiungi... Edit Modifica Remove Rimuovi Includes Inclusioni Include directories: Cartelle di inclusione: Up Su Down Giù Exclude Escludi Suppressions Suppression list: Add Defines: Definizioni: ProjectFileDialog Project file: %1 File di progetto: %1 Select include directory Seleziona la cartella da includere Select a directory to check Seleziona una cartella da scansionare 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 ResultsTree File File Severity Severità Line Linea Summary Riassunto Undefined file File indefinito [Inconclusive] [Inconcludente] debug debug 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 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 style stile error errore warning avviso performance performance portability portabilità information Informazione ResultsView %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. Summary Riassunto Message Messaggio Id Id No errors found, nothing to save. Nessun errore trovato, nulla da salvare. Failed to save the report. Salvataggio del report fallito. Results Risultati 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 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] [Predefinito] 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: Copy to Clipboard Copia negli Appunti 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 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 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.66/gui/cppcheck_ja.ts000066400000000000000000001740031236713773000166320ustar00rootroot00000000000000 About About Cppcheck cppcheckについて Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. CppcheckはC/C++ 静的コード解析ツールです. Copyright © 2007-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and 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) &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 が読み込めません 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 standard &Edit 編集(&E) Standard 標準(&S) Categories カテゴリ(&C) &License... ライセンス(&L)... A&uthors... 作者(&u)... &About... Cppcheckについて(&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 エラーを表示 Show S&cratchpad... Information 情報 Show information messages 情報メッセージを表示します。 Portability 移植可能性 Show portability warnings 潜在的な移植可能性の問題を示しています。 &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 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) Filter フィルター &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 ログ表示 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! 解析可能なファイルではありません You must close the project file before selecting new files or directories! 新しいファイル/ディレクトリを解析するには現在のプロジェクトを閉じてください Select directory to check チェック対象のディレクトリを選択 Quick Filter: クイックフィルタ: Select files to check チェック対象のファイルを選択 Found project file: %1 Do you want to load this project file instead? Found project files from the directory. Do you want to proceed checking without using any of these project files? File not found Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value 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. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 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: プロジェクト: 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? English 英語 Dutch オランダ語 Chinese (Simplified) Finnish フィンランド語 French フランス語 Italian Korean Spanish スペイン語 Swedish スウェーデン語 German ドイツ語 Russian ロシア語 Japanese Japanease 日本語 Serbian セルビア語 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 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 プロジェクト Root: ルート: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: パス: Add... 追加... Edit 編集 Remove 取り除く Includes Include directories: Up Down Exclude 除外する Suppressions Suppression list: Add Defines: Defines: ProjectFileDialog Project file: %1 プロジェクトファイル:%1 Select include directory includeディレクトリを選択 Select a directory to check 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 ResultsTree File ファイル Severity 警告種別 Line Summary 内容 Undefined file 未定義ファイル [Inconclusive] [結論の出ない] debug デバッグ Copy filename ファイル名をコピー Copy full path フルパスをコピー Copy message メッセージをコピー Copy message id Hide 非表示 Hide all with id 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 style スタイル error エラー warning 警告 performance パフォーマンス portability 移植可能性 information 情報 ResultsView Results 結果 No errors found, nothing to save. 警告/エラーが見つからなかったため、保存しません。 Failed to save the report. レポートの保存に失敗しました。 %p% (%1 of %2 files checked) 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 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" Enable inline suppressions inline抑制を有効 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 N/A Add a new application 新しいアプリケーションの追加 Modify an application アプリケーションの変更 [Default] [デフォルト] 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: 情報メッセージ Copy to Clipboard クリップボードにコピー 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 Project Settings プロジェクトの設定 Paths パス Include paths Defines Path selected 選択されたパス Number of files scanned スキャンしたファイルの数 Scan duration スキャン期間 Errors エラー 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.66/gui/cppcheck_ko.ts000066400000000000000000001275561236713773000166640ustar00rootroot00000000000000 About About Cppcheck Cppcheck 정보 Version %1 버전 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - 정적 C/C++ 코드 분석 도구. Copyright © 2007-2012 Daniel Marjamäki and cppcheck team. Copyright © 2007-2012 Daniel Marjamäki and cppcheck team. 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-2013 Daniel Marjamäki and cppcheck team. Copyright © 2007-2012 Daniel Marjamäki and cppcheck team. {2007-2013 ?} Copyright © 2007-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2012 Daniel Marjamäki and cppcheck team. {2007-2013 ?} {2007-2014 ?} 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 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 이 존재하지 않습니다! 최근 프로젝트 목록에서 파일을 제거하시겠습니까? Dutch 네덜란드어 English 영어 Finnish 핀란드어 French 프랑스어 German 독일어 Japanese 일본어 Korean 한국어 Russian 러시아어 Serbian 세르비아어 Spanish 스페인어 Swedish 스웨덴어 Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Italian Show S&cratchpad... Chinese (Simplified) Select files to check 검사할 파일 선택 Cppcheck GUI - Command line parameters C++ standard C standard C11 C11 C89 C89 C++03 C++03 Error 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) 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. File not found Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%1'. %2 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 내장 방식 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 Libraries: Suppressions Suppression list: Add Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. ProjectFileDialog Project file: %1 프로젝트 파일: %1 Select include directory Include 디렉토리 선택 Select a directory to check 검사할 디렉토리 선택 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) 불러오기 실패 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 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 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" SettingsDialog N/A N/A Add a new application 새 응용 프로그램 추가 Modify an application 응용 프로그램 편집 [Default] [기본] 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: 정보 메시지: 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 정보 메시지 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.66/gui/cppcheck_nl.ts000066400000000000000000001754471236713773000166660ustar00rootroot00000000000000 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-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright (C) 2007-2009 Daniel Marjamäki en 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 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 Standard Standaard &File &Bestand &View &Weergave &Toolbars &Werkbalken &Check &Controleer C++ standard C++standaard C standard C standaard &Edit Be&werken &License... &Licentie... A&uthors... A&uteurs... &About... &Over... &Files... &Bestanden... Check files Controleer bestanden Ctrl+F Ctrl+F &Directory... &Mappen... Check directory Controleer Map Ctrl+D Ctrl+D &Recheck files &Opnieuw controleren Ctrl+R Ctrl+R &Stop &Stop 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 &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 Platforms C++11 C99 Posix C11 C89 C++03 &Check all &Controleer alles Filter &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... &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 &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: 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 Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. 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 Error 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: 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? Finnish Fins English Engels Chinese (Simplified) Chinees (Vereenvoudigd) Dutch Nederlands French Frans Italian Italiaans Korean Koreaans Spanish Spaans Swedish Zweeds German Duits Russian Russisch Japanese Japanease Japans Serbian Servisch 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> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters Cppcheck GUI - Command lijn parameters Platforms Built-in Gemaakt in 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 Root: Hoofdmap: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Paden: Add... Toevoegen... Edit Bewerk Remove Verwijder Includes Inclusief Include directories: Include mappen: Up Omhoog Down Omlaag Exclude Exclusief Suppressions Suppression list: Add Defines: Omschrijft: ProjectFileDialog Project file: %1 Project Bestand %1 Select include directory Selecteer include map Select a directory to check Selecteer een map om te controleren 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 ResultsTree File Bestand Severity Ernst Line Regel Summary Overzicht Undefined file Niet gedefinieerd bestand [Inconclusive] [Onduidelijk] debug 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 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 style Stijlfouten error Fouten warning Waarschuwing performance Presentatie portability Portabiliteit information Informatie ResultsView %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. Summary Overzicht Message Bericht Id Id 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 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 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] [Standaard] 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: Copy to Clipboard Kopieer naar Clipbord 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 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 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.66/gui/cppcheck_ru.ts000066400000000000000000002115431236713773000166670ustar00rootroot00000000000000 About About Cppcheck О Cppcheck Version %1 Версия %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - программа для статического анализа кода на языках С/С++. Copyright © 2007-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and 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 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 Standard Стандартные &File &Файл &View &Вид &Toolbars &Панель инструментов &Check &Проверить C++ standard Стандарт C++ C standard Стандарт C &Edit &Правка &License... &Лицензия... A&uthors... &Авторы... &About... &О программе... &Files... &Файлы... Check files Проверить файлы Ctrl+F Ctrl+F &Directory... &Каталог... Check directory Проверка директории Ctrl+D Ctrl+D &Recheck files &Перепроверить файлы Ctrl+R Ctrl+R &Stop Остановить 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 Показать предупреждения переносимости &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 &Check all Отметить все Filter Фильтр &Uncheck all Сбросить все Collapse &all Свернуть все &Expand all Развернуть все &Standard Стандартные Standard items Стандартные элементы Toolbar Панель инструментов &Categories Категории Error categories Категории ошибок &Open XML... &Открыть XML... Open P&roject File... Открыть файл &проекта... &New Project File... &Новый файл проекта... &Log View Посмотреть &лог Log View Посмотреть лог C&lose Project File &Закрыть файл проекта &Edit Project File... &Изменить файл проекта... &Statistics &Статистика &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: Быстрый фильтр: 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 Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. 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 Выберите файлы для проверки Error 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: Проект: 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 не найден! Хотите удалить его из списка проектов? Finnish Финский English Английский Chinese (Simplified) Китайский (упрощенный) Dutch Голландский French Французский Italian Итальянский Korean Корейский Spanish Испанский Swedish Шведский German Немецкий Russian Русский Japanese Japanease Японский Serbian Сербский 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> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters Cppcheck GUI - параметры Командной строки Platforms Built-in Встроенная 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 Проект Root: Корневая директория: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Пути: Add... Добавить... Edit Изменить Remove Удалить Includes Пути для заголовочных файлов Include directories: Пути для поиска заголовочных файлов: Up Вверх Down Вниз Exclude Исключенные пути Suppressions Suppression list: Add Defines: Макросы: ProjectFileDialog Project file: %1 Файл проекта: %1 Select include directory Выберите директорию для поиска заголовочных файлов Select a directory to check Выберите директорию для проверки 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 ResultsTree File Файл Severity Важность Line Строка Summary Кратко Undefined file Неопределенный файл [Inconclusive] [Неубедительный] debug отлаживать Copy filename Скопировать имя файла Copy full path Скопировать полный путь Copy message Скопировать сообщение Copy message id Скопировать номер сообщения Hide Скрыть Hide all with id Скрыть все с id 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 style стиль error ошибка warning предупреждение performance производительность portability переносимость information информация ResultsView %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. Не удалост прочитать отчет. Summary Кратко Message Сообщение Id Id No errors found, nothing to save. Ошибки не найдены, нечего сохранять. Failed to save the report. Не удалось сохранить отчет. Results Результаты 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-подавление ошибок 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] [По умолчанию] 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: Информационные сообщения: 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%1 секунд and и Project Settings Настройки проекта Paths Пути Include paths Включенные пути Defines Макросы Path selected Выбранные пути Number of files scanned Количество просканированных файлов Scan duration Продолжительность сканирования Errors Ошибки 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.66/gui/cppcheck_sr.ts000066400000000000000000001731001236713773000166610ustar00rootroot00000000000000 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-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright (C) 2007-2009 Daniel Marjamäki and 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 LogView Checking Log Clear Save Log Text files (*.txt *.log);;All files (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" MainWindow Cppcheck Cppcheck Standard Standard &File &File &View &View &Toolbars &Check &Check C++ standard C standard &Edit &Edit &License... &License... A&uthors... A&uthors... &About... &About... &Files... &Files... Check files Ctrl+F Ctrl+F &Directory... &Directory... Check directory Ctrl+D Ctrl+D &Recheck files &Recheck files Ctrl+R Ctrl+R &Stop &Stop Stop checking Esc Esc &Save results to file... &Save results to file... Ctrl+S Ctrl+S &Quit &Quit &Clear results &Clear results &Preferences &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 &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 Gtk Gtk Posix Posix C11 C11 C89 C89 C++03 C++03 &Check all &Check all Filter &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... &New Project File... &Log View Log View C&lose Project File &Edit Project File... &Statistics &Contents Categories Style warnings 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: Found project file: %1 Do you want to load this project file instead? Found project files from the directory. Do you want to proceed checking without using any of these project files? File not found Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. 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 Error Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Open the report file Checking is running. Do you want to stop the checking and exit Cppcheck?. XML files version 1 (*.xml) XML files version 2 (*.xml) Text files (*.txt) Text files (*.txt) CSV files (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) 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? Finnish Finski English Engleski Chinese (Simplified) Kineski (Pojednostavljeni) Dutch Holandski French Francuski Italian Italijanski Korean Korejski Spanish Španski Swedish Švedski German Nemački Russian Ruski Japanese Japanski Serbian Srpski 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 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 Root: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Add... Edit Remove Includes Include directories: Up Down Exclude Suppressions Suppression list: Add Defines: ProjectFileDialog Project file: %1 Select include directory Select a directory to check 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 ResultsTree File File Severity Severity Line Line Summary Undefined file Undefined file [Inconclusive] debug Copy filename Copy filename Copy full path Copy full path Copy message Copy message id Hide Hide all with id 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 style Style error Error warning performance portability information ResultsView %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. Summary Message Id No errors found, nothing to save. No errors found, nothing to save. Failed to save the report. Failed to save the report. Results Results ScratchPad Scratchpad filename Check Settings Preferences Preferences General General Include paths: 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 Paths Edit 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 Advanced &Show inconclusive errors S&how internal warnings in log SettingsDialog N/A Add a new application Add a new application Modify an application Modify an application [Default] 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: Copy to Clipboard 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Project Settings Paths Include paths Defines Path selected Number of files scanned Scan duration Errors 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.66/gui/cppcheck_sv.ts000066400000000000000000001763241236713773000167000ustar00rootroot00000000000000 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-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. Copyright (C) 2007-2013 Daniel Marjamäki and 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 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 Standard Standard &File &Arkiv &View &Visa &Toolbars Verktygsfält &Check &Check C++ standard C++ standard C standard C standard &Edit &Redigera &License... &Licens... A&uthors... &Utvecklat av... &About... &Om... &Files... &Filer... Check files Analysera filer Ctrl+F Ctrl+F &Directory... &Katalog... Check directory Analysera mapp Ctrl+D Ctrl+D &Recheck files Starta &om check Ctrl+R Ctrl+R &Stop &Stoppa 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 &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 &Check all &Kryssa alla Filter Filter &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... &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 &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: 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 Bad XML Unexpected element Missing attribute Bad attribute Bad attribute value Failed to load the selected library '%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. 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 Error 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) 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: 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? Finnish Finska English Engelska Chinese (Simplified) Kinesiska (Förenklad) Dutch Nederländska French Franska Italian Italienska Korean Koreanska Spanish Spanska Swedish Svenska German Tyska Russian Ryska Japanese Japanease Japanska Serbian Serbiska 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> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters Cppcheck GUI - Command line parameters Platforms Built-in Generell 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 Root: Rot: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: Sökvägar: Add... Lägg till... Edit Redigera Remove Ta bort Includes Include Include directories: Include sökvägar Up Upp Down Ned Exclude Exkludera Suppressions Suppression list: Add Defines: Defines: ProjectFileDialog Project file: %1 Projektfil: %1 Select include directory Välj include sökväg Select a directory to check Välj mapp att analysera Select directory to ignore Välj sökväg att ignorera Add Suppression Select 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 ResultsTree File Fil Severity Typ Line Rad Summary Sammanfattning Undefined file Odefinierad fil [Inconclusive] [Inconclusive] debug debug 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 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 style stil error fel warning varning performance prestanda portability portabilitet information information ResultsView %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. Summary Sammanfattning Message Meddelande Id Id No errors found, nothing to save. Inga fel hittades, ingenting att spara. Failed to save the report. Misslyckades med att spara rapporten. Results Resultat 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 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] [Förvald] 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: Copy to Clipboard Kopiera 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 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 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.66/gui/cppcheck_zh_CN.ts000066400000000000000000001730551236713773000172470ustar00rootroot00000000000000 About About Cppcheck 关于 Cppcheck Version %1 版本 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - C/C++ 静态代码分析工具。 Copyright © 2007-2014 Daniel Marjamäki and cppcheck team. Copyright © 2007-2013 Daniel Marjamäki and cppcheck team. 版权所有 © 2007-2012 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 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 &Edit 编辑(&E) Standard 标准 Categories 分类 &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 显示错误 Show S&cratchpad... 显示便条(&C)... Information 信息 Show information messages 显示信息消息 Portability 移植可能性 Show portability warnings 显示可移植性警告 &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 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) Filter 滤器 &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 日志视图 There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. 加载编辑器应用程序设置出错。 这可能是因为 Cppcheck 不同版本间的设置有所不同。请检查(并修复)编辑器应用程序设置,否则编辑器程序可能不会正确启动。 No suitable files found to check! 未发现适合检查的文件! You must close the project file before selecting new files or directories! 在选择新的文件或目录之前,你必须先关闭此项目文件! Select directory to check 选择目录来检查 Quick Filter: 快速滤器: Select files to check 选择要检查的文件 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 Unexpected element Missing attribute Bad attribute Bad attribute value 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. 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: 项目: 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 未找到! 你要从最近使用的项目列表中删除此文件吗? English 英語 Dutch 荷兰语 Chinese (Simplified) Finnish 芬兰语 French 法语 Italian 意大利语 Korean 韩文 Spanish 西班牙语 Swedish 瑞典语 German 德语 Russian 俄语 Japanese Japanease 日语 Serbian 塞尔维亚语 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 内置 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 项目 Root: 根目录: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Paths: 路径: Add... 添加... Edit 编辑 Remove 移除 Includes 包含 Include directories: Include 目录: Up 向上 Down 向下 Exclude 排除 Suppressions Suppression list: Add Defines: 定义: ProjectFileDialog Project file: %1 项目文件: %1 Select include directory 选择 Include 目录 Select a directory to check 选择一个检查目录 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 加载翻译文件 ResultsTree File 文件 Severity 严重性 Line Summary 概要 Undefined file 未定义文件 [Inconclusive] [不确定的] debug 调试 Copy filename 复制文件名 Copy full path 复制完整路径 Copy message 复制消息 Copy message id 复制消息 ID Hide 隐藏 Hide all with id 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 style 风格 error 错误 warning 警告 performance 性能 portability 移植可能性 information 信息 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 Id 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 启用内联方案 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] [默认] 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: 信息: 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 包含路径 Defines 定义 Path selected 选中的路径 Number of files scanned 扫描的文件数 Scan duration 扫描时间 Errors 错误 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.66/gui/csvreport.cpp000066400000000000000000000033221236713773000165560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.files[error.files.size() - 1]); QString line = QString("%1,%2,").arg(file).arg(error.lines[error.lines.size() - 1]); line += QString("%1,%2").arg(GuiSeverity::toString(error.severity)).arg(error.summary); mTxtWriter << line << endl; } cppcheck-1.66/gui/csvreport.h000066400000000000000000000034541236713773000162310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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.66/gui/erroritem.cpp000066400000000000000000000030121236713773000165330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" ErrorItem::ErrorItem() : severity(Severity::none) , inconclusive(false) { } ErrorItem::ErrorItem(const ErrorLine &line) : file(line.file) , files(line.file) , errorId(line.errorId) , severity(line.severity) , inconclusive(line.inconclusive) , summary(line.summary) , message(line.message) { lines.append(line.line); } QString ErrorItem::ToString() const { QString str = file + " - " + errorId + " - "; if (inconclusive) str += "inconclusive "; str += GuiSeverity::toString(severity) +"\n"; str += summary + "\n"; str += message + "\n"; for (int i = 0; i < files.size(); i++) { str += " " + files[i] + ": " + QString::number(lines[i]) + "\n"; } return str; } cppcheck-1.66/gui/erroritem.h000066400000000000000000000047401236713773000162110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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(Severity::toString(severity).c_str()); } static Severity::SeverityType fromString(const QString &severity) { return Severity::fromString(severity.toStdString()); } }; /** * @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(); ErrorItem(const ErrorLine &line); /** * @brief Convert error item to string. * @return Error item as string. */ QString ToString() const; QString file; QStringList files; QString file0; QList lines; QString errorId; Severity::SeverityType severity; bool inconclusive; QString summary; QString message; }; Q_DECLARE_METATYPE(ErrorItem); /** * @brief A class containing error data for one shown error line. */ class ErrorLine { public: QString file; unsigned int line; QString errorId; bool inconclusive; Severity::SeverityType severity; QString summary; QString message; }; /// @} #endif // ERRORITEM_H cppcheck-1.66/gui/file.ui000066400000000000000000000030171236713773000153020ustar00rootroot00000000000000 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.66/gui/filelist.cpp000066400000000000000000000073071236713773000163510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" 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)) 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; } QStringList FileList::ApplyExcludeList() const { QStringList paths; foreach(QFileInfo item, mFileList) { QString name = QDir::fromNativeSeparators(item.canonicalFilePath()); if (!Match(name)) paths << name; } return paths; } bool FileList::Match(const QString &path) const { for (int i = 0; i < mExcludedPaths.size(); i++) { if (mExcludedPaths[i].endsWith('/')) { const QString pathexclude("/" + mExcludedPaths[i]); if (path.indexOf(pathexclude) != -1) return true; } else { if (path.endsWith(mExcludedPaths[i])) return true; } } return false; } cppcheck-1.66/gui/filelist.h000066400000000000000000000065331236713773000160160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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); protected: /** * @brief Return list of default filename extensions included. * @return list of default filename extensions included. */ static QStringList GetDefaultFilters(); /** * @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; /** * @brief Test if path matches any of the exclude filters. * @param path Path to test against exclude filters. * @return true if any of the filters matches, false otherwise. */ bool Match(const QString &path) const; private: QFileInfoList mFileList; QStringList mExcludedPaths; }; #endif // FILELIST_H cppcheck-1.66/gui/fileviewdialog.cpp000066400000000000000000000043151236713773000175240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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(); QString filestringdata(filedata); edit->setPlainText(filestringdata); } cppcheck-1.66/gui/fileviewdialog.h000066400000000000000000000033021236713773000171640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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); /** * @brief Format dialog title from filename. * * @param filename File to load. */ QString FormatTitle(const QString &filename); Ui::Fileview mUI; }; /// @} #endif // FILEVIEW_DIALOG_H cppcheck-1.66/gui/gui.cppcheck000066400000000000000000000004441236713773000163130ustar00rootroot00000000000000 cppcheck-1.66/gui/gui.pro000066400000000000000000000067571236713773000153500ustar00rootroot00000000000000TEMPLATE = app TARGET = cppcheck-gui CONFIG += warn_on debug DEPENDPATH += . \ ../lib INCLUDEPATH += . \ ../lib # In Qt 5 widgets are in separate module greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets } contains(LINKCORE, [yY][eE][sS]) { LIBS += -l../bin/cppcheck-core DEFINES += CPPCHECKLIB_IMPORT } LIBS += -L../externals DESTDIR = . RCC_DIR = temp MOC_DIR = temp OBJECTS_DIR = temp UI_DIR = temp 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 \ logview.ui \ main.ui \ projectfile.ui \ resultsview.ui \ scratchpad.ui \ settings.ui \ stats.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 \ logview.h \ mainwindow.h \ platforms.h \ project.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 \ xmlreportv1.h \ xmlreportv2.h SOURCES += aboutdialog.cpp \ application.cpp \ applicationdialog.cpp \ applicationlist.cpp \ checkstatistics.cpp \ checkthread.cpp \ common.cpp \ csvreport.cpp \ erroritem.cpp \ filelist.cpp \ fileviewdialog.cpp \ logview.cpp \ main.cpp \ mainwindow.cpp\ platforms.cpp \ project.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 \ xmlreportv1.cpp \ xmlreportv2.cpp win32 { DEFINES += _CRT_SECURE_NO_WARNINGS RC_FILE = cppcheck-gui.rc HEADERS += ../lib/version.h LIBS += -lshlwapi } contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++0x } cppcheck-1.66/gui/gui.qrc000066400000000000000000000022031236713773000153130ustar00rootroot00000000000000 icon.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-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 cppcheck-1.66/gui/help/000077500000000000000000000000001236713773000147535ustar00rootroot00000000000000cppcheck-1.66/gui/help/buildhelp.bat000066400000000000000000000002741236713773000174160ustar00rootroot00000000000000@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.66/gui/help/manual.html000066400000000000000000000441171236713773000171250ustar00rootroot00000000000000 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. Leaks

Looking for memory leaks and resource leaks is a key feature of Cppcheck. Cppcheck can detect many common mistakes by default. But through some tweaking you can improve the checking.


7.1. Userdefined allocation/deallocation functions

Cppcheck understands many common allocation and deallocation functions. But not all.

Here is example code that might leak memory or resources:

void foo(int x)
{
    void *f = CreateFred();
    if (x == 1)
        return;
    DestroyFred(f);
}

If you analyse that with Cppcheck it won't find any leaks:

cppcheck --enable=possibleError fred1.cpp

You can add some custom leaks checking by providing simple implementations for the allocation and deallocation functions. Write this in a separate file:

void *CreateFred()
{
    return malloc(100);
}

void DestroyFred(void *p)
{
    free(p);
}

When Cppcheck see this it understands that CreateFred will return allocated memory and that DestroyFred will deallocate memory.

Now, execute Cppcheck this way:

cppcheck --append=fred.cpp fred1.cpp

The output from cppcheck is:

Checking fred1.cpp...
[fred1.cpp:5]: (error) Memory leak: f

Chapter 8. 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 9. 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 10. Graphical user interface

10.1. Introduction

A Cppcheck GUI is available.

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


10.2. Check source code

Use the Check menu.


10.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.


10.4. Settings

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

More settings are available in Edit>Preferences.


10.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.66/gui/help/online-help.qhcp000066400000000000000000000006241236713773000200440ustar00rootroot00000000000000 online-help.qhp online-help.qch online-help.qch cppcheck-1.66/gui/help/online-help.qhp000066400000000000000000000006231236713773000177000ustar00rootroot00000000000000 cppcheck.sourceforge.net doc
manual.html cppcheck-1.66/gui/icon.png000066400000000000000000000052011236713773000154570ustar00rootroot00000000000000PNG  IHDRAAE HIDATxݛ PT,_QjE1P ՈHi5 H[#7 &6jj2:hjbTkLR4jkHR4Q?W };qw{ϻs} Zv߀\Yp ԁ20 WžwS@pAC!Z0@|};RBk&NB~~).!;x{;ɩ:+iLf9r[bQ%B JXNq*"ٳZ@%{,plCA$`LaJA{^GrE͵*Zhll$@)MB5hnFDɩ޽-\YiF*"{DpiԨrC"̜yrDThh<Ͷ3zЈ]K4oo 0[,Y><a r"1UI=Bn7|]Jw$B~9l`qP^.܁mn)ܓ PXX?\်w"d~EppKv[G*[[gkl+a*Z(a0 d HY}pã16m.Q[-~G*;5"ۺA $TFi3wQ| pEl߾u֬\Icl옳E`"X:P@IOo*fzDx"6lGIB 2CDX4cm!B,@y)uvP Ġ.w\> 66]K#4Z\Qxk4>P('GMk", uIeWlcbpГU,Xo$Q*& &Ayp3Xi 4O:Y@C4Z{ϊ= S FeC6j?9ʪ+A6WF;p%0#c(u/μݼ`kE@==[UbFhC{?' bKPP -[v1}>lY#h޼ߋkYOO?6 QI5~k4 tJf36|J%!a"8cԩ?cWUf֬_K):E`d?0Yhhd/YOX˖#jsk2 MTU6>>w K!xh8;Ynq6:.sE' M6ը|c{8Dku|+i 0c67Y:˙Mp (N3>pebn@z8D!@- wϕ;&@i p[io2w{]$1΁qv .tzcKߨ)񳈜'Y'Z͠j^bn]"])+Z.=fXE;?!ݿ^! WNcyȱ~Ee5h`-_h`R^N;ی6@kJ҃8z ^ E`\q (ZGOH8n씔 .V`RخHTk/bo=ex/ML'a3ÆlF!0o8ϥs8r+Y󐵥J_åqk* ~ُ5z)]$ image/svg+xml c ++ cppcheck-1.66/gui/images/000077500000000000000000000000001236713773000152705ustar00rootroot00000000000000cppcheck-1.66/gui/images/applications-development.png000066400000000000000000000023211236713773000230020ustar00rootroot00000000000000PNG  IHDRĴl;IDATx hUs!ִB\bZ%Xbf }AI!W-SB2Bv7՚@*Y"u~?9{ s9<̇ES CFS 4%2Tbp~tJ.txq+Ʃd93jHdp*^[K{ۋqFJ hq]bnrab+z ŽMi_"vƲV2X8H+Jҭ,pUȝGeW]+:WD $F1u -hڸuCƈ5[hDm J{(C+5RZSŠX/$}7³]si4{3O0Ơ XEh4h+hna#{?lܙlǫUdkR%<7O$ (]Z=LrA?Wө@:n[eU@cp㠽<6E7npJ!@%ߝ10Sc.tgjzE_xNj}H%P^-UQQ`<`hUB=kAvU*k :peA(7RyG .|):#5ƒhVcԸC]V8yJӃ >'0Fh|h֪\l[ٛoshP>!Qd~u*OѦjiY#Lк|p;fǿ i2ϠmuxSkN/uׅib>hl_K$އΙ!w<ݿek>S `S;ډH &MT_r}SeΉBZ)b?֧G}׉cqܺ}B2 z \`)B#:lʆv.5ˌ|]B򼋢LnC^ ދIqht`5]l4 pFH/ 䢄$&&~JʍJ#bR*}hU:d2Bnߺ;c1x^w,Mh1_Kd(4͒F>>R\uZ5^4iadD@&MCh3yQmbR06U9~BO$  evD>6XIbkF} 㣓hm6W$b >HW+%e#"2e6?4[Ӯ<|]: eζQl؂)Տ#|o +ɚΪoxDaKf=v`ubm[#W@KR6.U{3.hxx~FԚ~Hle$f g{f[ Zbz8X0wWxx%ψP:"fz;6B^Ȝ1ok /}{VY&#i~[ībbѴ4O+IENDB`cppcheck-1.66/gui/images/dialog-error.png000066400000000000000000000014771236713773000203750ustar00rootroot00000000000000PNG  IHDRĴl;IDATxՓ=hSQܟU64PSݝN. (8UWAA)ڟV!iMۚs|PZ/9>;/\ G8pƀ~`\`OGG9LZ |2p'?'XRg.]"yt~D|hQ5<{({ի8BjbBۨ2瑞dP8?U_]'L]NDQ[[h@ZKWEfd]bnL^22PY+saJtH60~SC'NYZFiczW)"!rخkcH~οj9B7b{M8LSlofn n&g{EmT;J&KH70-MmT*+x[ @H Pc"IVK+? r.Wc1uޞ7u$R*.ᙀea}<mmjhbZNDP$u9ʭ ؒ~ HX<n4J-@c R 77rP!p bo`.h18pH&d jKam\>ݞ/6ВHbp)`0 ^Y?iMU[ *P*@hPx! }pE1>M֔pQb# n@'["g[WDj7HIENDB`cppcheck-1.66/gui/images/dialog-information.png000066400000000000000000000021351236713773000215610ustar00rootroot00000000000000PNG  IHDRĴl;$IDATx}he}%^|U[vUg\EBŵT!*@L-&ENtVF[Yvd~IrwセapA ^~MXYBH+>@={ڀGi#GkX$IQ!K¿wn3B{'w<꟏:kJ$&a49*8? n?EWˆiw\KA c4%+ 0#_knU:Z#|2PL AɁ'"26>*Za2Mm|#\v!Med(%È;:,RBYA4Npoo%d1  jB8 cK(4p!ZBE%/1^g`l.O*EA1L %jSy)AiM Lȝɪ9e7ˍB&c0iVʹ7x\*+sv#?FO#{fz$v+V`vzoCOZKj,!{F@?L@Vʽ^i>##wB7fB6X :Z[qWZ@l0_Ӫ>V)j\ykv}v3ß0 i=XhÆMd(9)86WK~2Pv ]'abm HŶ]t,J/"ظxWw J6\`Ay/ˉo|*Vp帚Nu@m0kº΃LO VFc,["r^JZ< XfFϞJRf3?~8vtwCO3| eu*c@ܼxa!GQmiy࣮jhk0GC%cFŻXb}g<oCQ!tW](d 5Cv W.wpxIENDB`cppcheck-1.66/gui/images/dialog-warning.png000066400000000000000000000015401236713773000207000ustar00rootroot00000000000000PNG  IHDRĴl;'IDATxڥKQƟss6+SK4T V TAoD*2뮂Bn Ҩ,, J,NN_ vw;)eI>{\9JP'c̰h\O\Sx9*l `SSS5 \Ӵ Z,>JodKG"iy*5/?%Vk4gf?ֆem&z)1 SViGFM\WfaD"d2@ӧ'V]in'E(w65:(B pޭ}rI477kĸ5SB Ձl.:ي D4 /@cx_M{SNMn0MԳRVE0"82瓌N507Xⵔ(h z7(P^EvuJy#/gkEE"t<6/yA\B*IENDB`cppcheck-1.66/gui/images/edit-clear.png000066400000000000000000000021571236713773000200140ustar00rootroot00000000000000PNG  IHDRĴl;6IDATx^}hUu?{wv763$%64R#WыQ̬%AB4ʢ72-Vڦ._n6{ɻt__xs>/y6>*{zvrqc:n]Ȓ+ycJͬ\oӓ6z-/-$jc.Yb n l%q43\NrnܾvGV4yaS8 WDt6PRVtx[7ҿ)R7X:ӾnQ5:2/}!͟_L&etzu>g[U/5#˛ovx[iCMHI( (IY}l=[ߙHX^/l; [zZ Y3$YD#p [ß$~9mK(Ya(v:|^9@սD YӸD 9zX7˾>@4ʼn׬+Ug\oиteUbY ,9? $#?+ 8 k~*̗# A,) KMbq$)5^yJPM9Y)1t`a3)cƥDs `4h:p0@Ξم<ȯu=0Ho: Jii`5IQNEdO^}Efb։qO#MRS8{Ģ{jV)'鄘#eGO[ڇnmo-Eb^].e|JǭX(v [Q#wt?^Џqod+z2a= ָЊIjꌊmwڤގ5: 4zZ=DHl,6tӽ+TqRhJ eU;tNZp v@$nP_yRl*Be3U$tg'yYP (|;B}^"w/\QxIENDB`cppcheck-1.66/gui/images/go-home.png000066400000000000000000000015601236713773000173330ustar00rootroot00000000000000PNG  IHDRj PLTE..,,uuuUWSuuuuu愆|vy}%%dd}4eↈywux|^^FFЙ][ޢ[[egc99{LJ"!uvv~{cc叐LLwxzġ~UU&&qxﺺɵruzJv&&ZX&&ux{QQvy|ep''llnvff_us5)tRNS3 " S\0_l5IRVGD?/ JhH7IDATx^mcwLqgj^Y۶m^7I9m˨4) HV7;BC}uV>l"])]u7H/ GW+;HD%_جpO)ԉHAxV;==4yao O1y5c0̸B!җ*ҟNg-zWA,[4~#*AJ̀Chjp}-{'1 k%9rBeOo_?<\>1֤+4&%he,kL:b5srL_ sIENDB`cppcheck-1.66/gui/images/go-next.png000066400000000000000000000015001236713773000173530ustar00rootroot00000000000000PNG  IHDRĴl;IDATxŔhMaƟmAk+_ʊb JIi""M*ͪ5HMBc 6afmsl3" 9P̍P 폰a)Sf$hw4-/F[~,Ũ-x>n)Fr ͝f& *+D !Ҙ1.YrBu#,J NrGN_g,J z:>̗H?1cOx٘v $rjۭyנS_^TUH$8yu )MFݷp ײjR$ļ|Gs cI mFUe_ @(Bx!7 UutЍ4Jߴ\x~Q߆ .!! ȋݖ 'okꯪJ3HE%QakJo8^T3P, /}6*E|A(`*Vjj1}QҩD5jNY3"mR>÷9\P!{m⾑x:?4b*%1akp_5.[uҚ7}}ݏ6E.^}]pyg׽ѷӟӳq㨔bno?#٨շF;i:6e5mP,o51!ѱk;`yqsnkks7?f'XAk,ADozU䆣=k^-g4#RqE>3pqUQYNqw<3e7L$ԔHjV'*(8b{^ 1R%ʾHU,bcA1V#hh ٹ㯚'&޽pӺn.L l@lIZ,M52,Q;~kkz:""ldp!JL]$_%UH$'Tqf<}[:Ǩgq*WRߘ̜EDZuA8~ݍy9ݵe}4R. E`n4E$u4bo_P'ϟ Ś ղF1@< @9P pOPx;](xyX_&s4<`@| -e/V~P<,KP WVE4PU/"B#ҢF5N*3?(;*s s(i-RBa$L؀4еzB҈L)]F7qXaIENDB`cppcheck-1.66/gui/images/help-browser.png000066400000000000000000000023161236713773000204110ustar00rootroot00000000000000PNG  IHDRĴl;IDATxڍ,guYekuVGj[unK6n 6eC*I 0FqgnPV -Y>-)M^=|rgY.Bز޾0!'AD7SWI,IEge I{*efMKʴv?P Hg&ȫ S3 v:LAgʈד@ޛE^U7>qvQqOWH>uN׮YiKt6i=qM/.b!WP-a45'S\G\\O(UfYrdL1ޓW s,9ȮA1-G-sX%LZ <|/upuZO9šx':-d՚R6p/KH9JjI9m $gl=\#%6`bZ]sbʼnY-UKvoOͫũU VN*@9 =Z˓Jidi>pbGp*Bھт.D"Lr?+^ Z_Se&_.# !ۓ9XDm֠L̨0pHJi?2Oo/EY"lu@SP'IRG!1iWq.}(iX?rEOgHUQE"#i3[!Ӻ߆+ɡFElݮ&9.rWgKkj?P؊ԍwJǜjMmڐF+Y%M7p Sф~HLEҡUC%n~k ;G^brqjsf-wrYK}[M;F=bslz:mcu;acض][cpp{i#īě>aNX&xxkt|xIENDB`cppcheck-1.66/gui/images/media-floppy.png000066400000000000000000000012451236713773000203660ustar00rootroot00000000000000PNG  IHDRĴl;lIDATxϋQ?.FPQ6kR(XJ)d3%abcRL3-5wW{/ԩSN9} L3p̮4sl96H(d3gkP`53|΁ww/stj@=ɝɛ) 䌟H[ y)v C/'vriZѰY$JArFE.()'N)(\NʌSN\s'N)Gy(*;oQK EDQU,*p0W[1q8Hl; y ccc4MFԼw:i0Jq"ybb"sssdfgg !KGEyD~rv;&,v!? PW!زTRJVWWKi,עׁ*WNEF dNw6KYLyWȑ@"=G`ee%Y[[[Gƅ8fX : \0\ L/<:f\c'u"XyF܄nj@l JJ1IIENDB`cppcheck-1.66/gui/images/openproject.png000066400000000000000000000020051236713773000203230ustar00rootroot00000000000000PNG  IHDR szzIDATxMk$EƟydqW$"Sς^?E$^&Bބ!01ndc:3z`t$ Rt"6^%-²rRJ{ H_T&PZoyޯý+Faΐ$RVWWV" (cdyy J)m X綅0GGG?)`a I.xZvNNA)_i}'yJ1kcNffɲ\gG:  XJE*$l^ Waԝ- g?2w6f_qqqEᅪ'o,ߩQ"inle2ŗ>{n?(::"$t2$68g 5tV`851Vh)Lay,#t]0EЌ(EL V'd,9)%O \ BF:)+@ 0)ԈBZ)yVvX^Cf%x|e p.s !sZk6Lq,vτF`BJK. 5Pa Ak܍cƭ Ɣ??Z5OS$q4n!P5cȹxGPB@6j+$ ɘe.BrY]bƈ L*8J~}|4~1@ &5R;*(%8砄f۽.GQN E9,5,6jUb0A -fK?ǠUmݦ}!f4S _DqUb I_ajZD 7P J)Ί$D_vঀf$#)lxr2gW@IENDB`cppcheck-1.66/gui/images/preferences-system.png000066400000000000000000000016461236713773000216300ustar00rootroot00000000000000PNG  IHDRj PLTE4eqqq,W$FqX^rch:AC6"=Hpw (`5|+W xf/B_"7N̪ | Ȃ@=' O~Aw%puQ:6 zh" .3Zk0."Q}۾}l:VKXAD"@(׋]])_{~,u;5PFd)a3PY]Fj"Nݻ?(>BHR ~ΑT8R\vM1Y_~axggQ E_4K1UUsjS]Ai{z{2 ,RwL[Z9iYyt]XiYJ|ssxT⬖Y6s,ssm@6EEDoWO~h MܲXug99bAѻ}{׍Wӛ r 䉜 ք3Cn.rvHW6>)./Mii-,ӧdk2ٔ{Ji` `JMmZ,z;in_&JFJ7^cGJmF9A&GڵKް-"bYfɲ$A+7D".& @2IC&O(!U O2v?8Q!IENDB`cppcheck-1.66/gui/images/scratchpad.png000066400000000000000000000004111236713773000201060ustar00rootroot00000000000000PNG  IHDR Tg-PLTEIIIOO>qtM̔ɰ|tRNS@fIDATx!@EgvѠ"<l- $+TT5{9:^~k}`Ӳf6u0 C 0 C=p`o0$'eWlIENDB`cppcheck-1.66/gui/images/showerrors.png000066400000000000000000000014761236713773000202230ustar00rootroot00000000000000PNG  IHDRĴl;IDATxՓ=hSQܟU6/ 8iEp$:88 ஢.u7H[!iJ657x|\p*}` z ` 852H=ˡduhVxs9fgKn_@R/\Nh/->Zi(ar|o}yŽwN@_޺31hJ+tZ/a{Q+cZ#ubϵ;wѹ9Z)ҕsYGc3C|bL^22PY+saBtPv >C{ae!VZh 0~a%ƐgZL:^:<۶0͚#^QLmWa\dPr6rub_ `c)j^v2jqB;^fK~,nwbF2DQŕR[77.^D䯓'+( c8IvfR!4F?Z-R).?ҮV%P8NOX~oL0°rHx0M$ WU%F6j} ДHbp)Q 7`]7Zo h_<@ (C]pW4pTaYl6[C&VEȒ3!OHrn}wn P Yj|IENDB`cppcheck-1.66/gui/images/showperformance.png000066400000000000000000000017151236713773000212040ustar00rootroot00000000000000PNG  IHDRj %PLTEhjekmhprmvvtlniHJF^_]<>; Jr/fcpo5q^sm󥶽j1kpn}oDnkx̗幾n}n|sslyrߖrwtBmjuo~ly喚rw#N!IyEs?l٘GvwusSo+Jscsczo`wjXZVnfI^{[daBIejalDM[o~^jlc3oigecľiq1ly{7uron~]%Up-c1k3oqt;y7u5o/i-c)\'U)^/d5r;x?;}9x3k-c)Zý_wlzAn\+`'Y^)\ev9cMdĿſ˔ tRNSMF?.))))??KLGeEIDATx^c` <"ADq 몪Z(i {fB2SKMF}PT8YRd\XJ6 0(8$t\xm22n<}|K儗*X)*#'($,NNMMpfVcNnpArQqIR! U5*+V…V+utvuAՅ'LT4yieg̜%4,<-/ذq-l | "@ 6vV ###0 :cv'1IENDB`cppcheck-1.66/gui/images/showstylewarnings.png000066400000000000000000000021351236713773000216110ustar00rootroot00000000000000PNG  IHDRĴl;$IDATx}he}%^|U[vUg\EBŵT!*@L-&ENtVF[Yvd~IrwセapA ^~MXYBH+>@={ڀGi#GkX$IQ!K¿wn3B{'w<꟏:kJ$&a49*8? n?EWˆiw\KA c4%+ 0#_knU:Z#|2PL AɁ'"26>*Za2Mm|#\v!Med(%È;:,RBYA4Npoo%d1  jB8 cK(4p!ZBE%/1^g`l.O*EA1L %jSy)AiM Lȝɪ9e7ˍB&c0iVʹ7x\*+sv#?FO#{fz$v+V`vzoCOZKj,!{F@?L@Vʽ^i>##wB7fB6X :Z[qWZ@l0_Ӫ>V)j\ykv}v3ß0 i=XhÆMd(9)86WK~2Pv ]'abm HŶ]t,J/"ظxWw J6\`Ay/ˉo|*Vp帚Nu@m0kº΃LO VFc,["r^JZ< XfFϞJRf3?~8vtwCO3| eu*c@ܼxa!GQmiy࣮jhk0GC%cFŻXb}g<oCQ!tW](d 5Cv W.wpxIENDB`cppcheck-1.66/gui/images/showwarnings.png000066400000000000000000000015401236713773000205270ustar00rootroot00000000000000PNG  IHDRĴl;'IDATxڥKQƟss6+SK4T V TAoD*2뮂Bn Ҩ,, J,NN_ vw;)eI>{\9JP'c̰h\O\Sx9*l `SSS5 \Ӵ Z,>JodKG"iy*5/?%Vk4gf?ֆem&z)1 SViGFM\WfaD"d2@ӧ'V]in'E(w65:(B pޭ}rI477kĸ5SB Ձl.:ي D4 /@cx_M{SNMn0MԳRVE0"82瓌N507Xⵔ(h z7(P^EvuJy#/gkEE"t<6/yA\B*IENDB`cppcheck-1.66/gui/images/text-x-generic.png000066400000000000000000000007111236713773000206400ustar00rootroot00000000000000PNG  IHDRj PLTEklidtRNS &*67@ACEFbP˰IDATx^u@Fand0$/fN^c^3,C#Bm 7Q\F#"ZQۀZ$Nih9׹e3&LWV 8I` \mV;\t*-ENϾkĉpz&}2[,7nOH>3#IENDB`cppcheck-1.66/gui/images/utilities-system-monitor.png000066400000000000000000000017151236713773000230240ustar00rootroot00000000000000PNG  IHDRj %PLTEhjekmhprmvvtlniHJF^_]<>; Jr/fcpo5q^sm󥶽j1kpn}oDnkx̗幾n}n|sslyrߖrwtBmjuo~ly喚rw#N!IyEs?l٘GvwusSo+Jscsczo`wjXZVnfI^{[daBIejalDM[o~^jlc3oigecľiq1ly{7uron~]%Up-c1k3oqt;y7u5o/i-c)\'U)^/d5r;x?;}9x3k-c)Zý_wlzAn\+`'Y^)\ev9cMdĿſ˔ tRNSMF?.))))??KLGeEIDATx^c` <"ADq 몪Z(i {fB2SKMF}PT8YRd\XJ6 0(8$t\xm22n<}|K儗*X)*#'($,NNMMpfVcNnpArQqIR! U5*+V…V+utvuAՅ'LT4yieg̜%4,<-/ذq-l | "@ 6vV ###0 :cv'1IENDB`cppcheck-1.66/gui/images/view-refresh.png000066400000000000000000000023451236713773000204100ustar00rootroot00000000000000PNG  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.66/gui/logview.cpp000066400000000000000000000050551236713773000162100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "logview.h" LogView::LogView(QWidget *parent) : QWidget(parent) { mUI.setupUi(this); setWindowFlags(Qt::Tool); mUI.mButtonBox->button(QDialogButtonBox::Reset)->setText(tr("Clear")); connect(mUI.mButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(CloseButtonClicked())); connect(mUI.mButtonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(ClearButtonClicked())); connect(mUI.mButtonBox->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(SaveButtonClicked())); QSettings settings; resize(settings.value(SETTINGS_LOG_VIEW_WIDTH, 400).toInt(), settings.value(SETTINGS_LOG_VIEW_HEIGHT, 300).toInt()); } LogView::~LogView() { QSettings settings; settings.setValue(SETTINGS_LOG_VIEW_WIDTH, size().width()); settings.setValue(SETTINGS_LOG_VIEW_HEIGHT, size().height()); } void LogView::AppendLine(const QString &line) { mUI.mLogEdit->appendPlainText(line); } void LogView::CloseButtonClicked() { close(); } void LogView::ClearButtonClicked() { mUI.mLogEdit->clear(); } void LogView::SaveButtonClicked() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save Log"), "", tr("Text files (*.txt *.log);;All files (*.*)")); if (!fileName.isEmpty()) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::warning(this, tr("Cppcheck"), tr("Could not open file for writing: \"%1\"").arg(fileName)); return; } QTextStream out(&file); out << mUI.mLogEdit->toPlainText(); } } cppcheck-1.66/gui/logview.h000066400000000000000000000030421236713773000156470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LOGVIEW_H #define LOGVIEW_H #include #include "ui_logview.h" /// @addtogroup GUI /// @{ /** * @brief A tool window that shows checking log. * */ class LogView : public QWidget { Q_OBJECT public: LogView(QWidget *parent = 0); ~LogView(); /** * @brief Append new log file to view. * @param line String to add. * */ void AppendLine(const QString &line); protected slots: /** * @brief Called when close button is clicked. * */ void CloseButtonClicked(); /** * @brief Called when clear button is clicked. * */ void ClearButtonClicked(); /** * @brief Called when save button is clicked. * */ void SaveButtonClicked(); private: Ui::LogView mUI; }; /// @} #endif // LOGVIEW_H cppcheck-1.66/gui/logview.ui000066400000000000000000000017661236713773000160500ustar00rootroot00000000000000 LogView 0 0 400 300 Qt::NoContextMenu Checking Log false true QDialogButtonBox::Close|QDialogButtonBox::Reset|QDialogButtonBox::Save cppcheck-1.66/gui/main.cpp000066400000000000000000000107721236713773000154620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" void ShowUsage(); void ShowVersion(); bool CheckArgs(const QStringList &args); int main(int argc, char *argv[]) { 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(":icon.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() 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; } 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= Specify directory where GUI datafiles are located (translations, cfg)"); #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 } 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.66/gui/main.ui000066400000000000000000000461271236713773000153200ustar00rootroot00000000000000 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 &Check 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... Check files Check files Ctrl+F :/icon.png:/icon.png &Directory... Check directory Check directory Ctrl+D :/images/view-refresh.png:/images/view-refresh.png &Recheck files Ctrl+R :/images/process-stop.png:/images/process-stop.png &Stop Stop checking Stop checking 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 warnings Show style warnings Show style warnings true :/images/showerrors.png:/images/showerrors.png Errors 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 Show S&cratchpad... &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 Performance 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 &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 Platforms false true true C++11 true true C99 true Posix true C11 true C89 true C++03 ResultsView QWidget
resultsview.h
1
cppcheck-1.66/gui/mainwindow.cpp000066400000000000000000001320361236713773000167100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mainwindow.h" #include "cppcheck.h" #include "applicationlist.h" #include "aboutdialog.h" #include "common.h" #include "threadhandler.h" #include "fileviewdialog.h" #include "projectfile.h" #include "project.h" #include "report.h" #include "scratchpad.h" #include "statsdialog.h" #include "settingsdialog.h" #include "threadresult.h" #include "translationhandler.h" #include "logview.h" #include "filelist.h" #include "showtypes.h" static const QString OnlineHelpURL("http://cppcheck.sourceforge.net/manual.html"); MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : mSettings(settings), mApplications(new ApplicationList(this)), mTranslation(th), mLogView(NULL), mScratchPad(NULL), mProject(NULL), mPlatformActions(new QActionGroup(this)), mCStandardActions(new QActionGroup(this)), mCppStandardActions(new QActionGroup(this)), mExiting(false) { mUI.setupUi(this); mUI.mResults->Initialize(mSettings, mApplications); mThread = new ThreadHandler(this); // Filter timer to delay filtering results slightly while typing mFilterTimer = new QTimer(this); mFilterTimer->setInterval(500); mFilterTimer->setSingleShot(true); connect(mFilterTimer, SIGNAL(timeout()), this, SLOT(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, SIGNAL(returnPressed()), this, SLOT(FilterResults())); connect(mUI.mActionQuit, SIGNAL(triggered()), this, SLOT(close())); connect(mUI.mActionCheckFiles, SIGNAL(triggered()), this, SLOT(CheckFiles())); connect(mUI.mActionCheckDirectory, SIGNAL(triggered()), this, SLOT(CheckDirectory())); connect(mUI.mActionSettings, SIGNAL(triggered()), this, SLOT(ProgramSettings())); connect(mUI.mActionClearResults, SIGNAL(triggered()), this, SLOT(ClearResults())); connect(mUI.mActionOpenXML, SIGNAL(triggered()), this, SLOT(OpenResults())); connect(mUI.mActionShowStyle, SIGNAL(toggled(bool)), this, SLOT(ShowStyle(bool))); connect(mUI.mActionShowErrors, SIGNAL(toggled(bool)), this, SLOT(ShowErrors(bool))); connect(mUI.mActionShowWarnings, SIGNAL(toggled(bool)), this, SLOT(ShowWarnings(bool))); connect(mUI.mActionShowPortability, SIGNAL(toggled(bool)), this, SLOT(ShowPortability(bool))); connect(mUI.mActionShowPerformance, SIGNAL(toggled(bool)), this, SLOT(ShowPerformance(bool))); connect(mUI.mActionShowInformation, SIGNAL(toggled(bool)), this, SLOT(ShowInformation(bool))); connect(mUI.mActionCheckAll, SIGNAL(triggered()), this, SLOT(CheckAll())); connect(mUI.mActionUncheckAll, SIGNAL(triggered()), this, SLOT(UncheckAll())); connect(mUI.mActionCollapseAll, SIGNAL(triggered()), mUI.mResults, SLOT(CollapseAllResults())); connect(mUI.mActionExpandAll, SIGNAL(triggered()), mUI.mResults, SLOT(ExpandAllResults())); connect(mUI.mActionShowHidden, SIGNAL(triggered()), mUI.mResults, SLOT(ShowHiddenResults())); connect(mUI.mActionViewLog, SIGNAL(triggered()), this, SLOT(ShowLogView())); connect(mUI.mActionViewStats, SIGNAL(triggered()), this, SLOT(ShowStatistics())); connect(mUI.mActionRecheck, SIGNAL(triggered()), this, SLOT(ReCheck())); connect(mUI.mActionStop, SIGNAL(triggered()), this, SLOT(StopChecking())); connect(mUI.mActionSave, SIGNAL(triggered()), this, SLOT(Save())); // About menu connect(mUI.mActionAbout, SIGNAL(triggered()), this, SLOT(About())); connect(mUI.mActionLicense, SIGNAL(triggered()), this, SLOT(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, SIGNAL(triggered()), this, SLOT(ShowAuthors())); connect(mThread, SIGNAL(Done()), this, SLOT(CheckDone())); connect(mUI.mResults, SIGNAL(GotResults()), this, SLOT(ResultsAdded())); connect(mUI.mResults, SIGNAL(ResultsHidden(bool)), mUI.mActionShowHidden, SLOT(setEnabled(bool))); connect(mUI.mMenuView, SIGNAL(aboutToShow()), this, SLOT(AboutToShowViewMenu())); // File menu connect(mUI.mActionNewProjectFile, SIGNAL(triggered()), this, SLOT(NewProjectFile())); connect(mUI.mActionOpenProjectFile, SIGNAL(triggered()), this, SLOT(OpenProjectFile())); connect(mUI.mActionShowScratchpad, SIGNAL(triggered()), this, SLOT(ShowScratchpad())); connect(mUI.mActionCloseProjectFile, SIGNAL(triggered()), this, SLOT(CloseProjectFile())); connect(mUI.mActionEditProjectFile, SIGNAL(triggered()), this, SLOT(EditProjectFile())); connect(mUI.mActionHelpContents, SIGNAL(triggered()), this, SLOT(OpenHelpContents())); LoadSettings(); mThread->Initialize(mUI.mResults); FormatAndSetTitle(); EnableCheckButtons(true); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); mUI.mActionRecheck->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] = NULL; // The separator mUI.mActionProjectMRU->setVisible(false); UpdateMRUMenuItems(); QStringList args = QCoreApplication::arguments(); //Remove the application itself args.removeFirst(); if (!args.isEmpty()) { HandleCLIParams(args); } 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.mMenuCheck->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); // 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 mLogView; delete mProject; delete mScratchPad; } void MainWindow::HandleCLIParams(const QStringList ¶ms) { if (params.contains("-p")) { const int ind = params.indexOf("-p"); if ((ind + 1) < params.length()) LoadProjectFile(params[ind + 1]); } else if (params.contains("-l")) { QString logFile; const int ind = params.indexOf("-l"); if ((ind + 1) < params.length()) logFile = params[ind + 1]; if (params.contains("-d")) { QString checkedDir; const int ind = params.indexOf("-d"); if ((ind + 1) < params.length()) checkedDir = params[ind + 1]; LoadResults(logFile, checkedDir); } else { LoadResults(logFile); } } else DoCheckFiles(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)); 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 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); 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(); } } 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_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()); mApplications->SaveSettings(); mSettings->setValue(SETTINGS_LANGUAGE, mTranslation->GetCurrentLanguage()); mUI.mResults->SaveSettings(mSettings); } void MainWindow::DoCheckFiles(const QStringList &files) { if (files.isEmpty()) { return; } ClearResults(); FileList pathList; pathList.AddPathList(files); if (mProject) pathList.AddExcludeList(mProject->GetProjectFile()->GetExcludedPaths()); 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 check!"), 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 (mProject) qDebug() << "Checking project file" << mProject->GetProjectFile()->GetFilename(); mThread->Check(checkSettings, false); } void MainWindow::CheckCode(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()); CheckDone(); } QStringList MainWindow::SelectFilesToCheck(QFileDialog::FileMode mode) { if (mProject) { 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 check"), GetPath(SETTINGS_LAST_CHECK_PATH)); 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 check"), 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::CheckFiles() { DoCheckFiles(SelectFilesToCheck(QFileDialog::ExistingFiles)); } void MainWindow::CheckDirectory() { QStringList dir = SelectFilesToCheck(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 { DoCheckFiles(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 checking 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) { DoCheckFiles(dir); } } } else { DoCheckFiles(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 (mProject) { QString path = QFileInfo(mProject->GetProjectFile()->GetFilename()).canonicalPath(); ret = library->load(NULL, (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(NULL, (appPath+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(NULL, (appPath+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; // Try to load the library from the cfg subfolder.. const QString datadir = mSettings->value("DATADIR", QString()).toString(); if (!datadir.isEmpty()) { ret = library->load(NULL, (datadir+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(NULL, (datadir+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; } return ret; } Settings MainWindow::GetCppcheckSettings() { Settings result; // If project file loaded, read settings from it if (mProject) { ProjectFile *pfile = mProject->GetProjectFile(); QStringList dirs = pfile->GetIncludeDirs(); AddIncludeDirs(dirs, result); QStringList defines = pfile->GetDefines(); QString define; foreach(define, defines) { if (!result.userDefines.empty()) result.userDefines += ";"; result.userDefines += define.toStdString(); } QStringList libraries = pfile->GetLibraries(); foreach(QString library, libraries) { const QString filename = library + ".cfg"; const Library::Error error = LoadLibrary(&result.library, filename); if (error.errorcode != Library::ErrorCode::OK) { 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::BAD_ELEMENT: errmsg = tr("Unexpected element"); break; case Library::ErrorCode::MISSING_ATTRIBUTE: errmsg = tr("Missing attribute"); break; case Library::ErrorCode::BAD_ATTRIBUTE: errmsg = tr("Bad attribute"); break; case Library::ErrorCode::BAD_ATTRIBUTE_VALUE: errmsg = tr("Bad attribute value"); 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)); } } QStringList suppressions = pfile->GetSuppressions(); foreach(QString suppression, suppressions) { result.nomsg.addSuppressionLine(suppression.toStdString()); } // Only check the given -D configuration if (!defines.isEmpty()) result._maxConfigs = 1; } // 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"); result.debug = false; result.debugwarnings = mSettings->value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool(); result._errorsOnly = 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(); result.standards.cpp = mSettings->value(SETTINGS_STD_CPP11, true).toBool() ? Standards::CPP11 : Standards::CPP03; 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(); bool std = (LoadLibrary(&result.library, "std.cfg").errorcode == Library::ErrorCode::OK); bool posix = true; if (result.standards.posix) posix = (LoadLibrary(&result.library, "posix.cfg").errorcode == Library::ErrorCode::OK); if (!std || !posix) QMessageBox::warning(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.").arg(!std ? "std.cfg" : "posix.cfg")); if (result._jobs <= 1) { result._jobs = 1; } return result; } void MainWindow::CheckDone() { if (mExiting) { close(); return; } mUI.mResults->CheckingFinished(); EnableCheckButtons(true); mUI.mActionSettings->setEnabled(true); mUI.mActionOpenXML->setEnabled(true); EnableProjectActions(true); EnableProjectOpenActions(true); mPlatformActions->setEnabled(true); mCStandardActions->setEnabled(true); mCppStandardActions->setEnabled(true); mUI.mActionPosix->setEnabled(true); if (mScratchPad) mScratchPad->setEnabled(true); if (mUI.mResults->HasResults()) { mUI.mActionClearResults->setEnabled(true); mUI.mActionSave->setEnabled(true); } for (int i = 0; i < MaxRecentProjects + 1; i++) { if (mRecentProjectActs[i] != NULL) mRecentProjectActs[i]->setEnabled(true); } // Notify user - if the window is not active - that check is ready QApplication::alert(this, 3000); } 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); mUI.mActionPosix->setEnabled(false); if (mScratchPad) mScratchPad->setEnabled(false); for (int i = 0; i < MaxRecentProjects + 1; i++) { if (mRecentProjectActs[i] != NULL) 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()); const QString newLang = mSettings->value(SETTINGS_LANGUAGE, "en").toString(); SetLanguage(newLang); } } void MainWindow::ReCheck() { const QStringList files = mThread->GetReCheckFiles(); if (files.empty()) return; // Clear details, statistics and progress mUI.mResults->Clear(false); // 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 (mProject) qDebug() << "Rechecking project file" << mProject->GetProjectFile()->GetFilename(); mThread->Check(GetCppcheckSettings(), true); } void MainWindow::ClearResults() { mUI.mResults->Clear(true); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->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()) { mUI.mResults->Clear(true); 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.mActionCheckFiles->setEnabled(enable); if (!enable || mThread->HasPreviousFiles()) mUI.mActionRecheck->setEnabled(enable); mUI.mActionCheckDirectory->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("Checking is running.\n\n" \ "Do you want to stop the checking 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::Save() { QString selectedFilter; const QString filter(tr("XML files version 2 (*.xml);;XML files version 1 (*.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 version 1 (*.xml)")) { type = Report::XML; if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) selectedFile += ".xml"; } else if (selectedFilter == tr("XML files version 2 (*.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::XML; 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(); delete mLogView; mLogView = 0; } } void MainWindow::AboutToShowViewMenu() { mUI.mActionToolBarMain->setChecked(mUI.mToolBarMain->isVisible()); mUI.mActionToolBarView->setChecked(mUI.mToolBarView->isVisible()); mUI.mActionToolBarFilter->setChecked(mUI.mToolBarFilter->isVisible()); } void MainWindow::StopChecking() { mThread->Stop(); mUI.mResults->DisableProgressbar(); } 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:") + QString(" ") + filename); AddProjectMRU(filePath); mUI.mActionCloseProjectFile->setEnabled(true); mUI.mActionEditProjectFile->setEnabled(true); delete mProject; mProject = new Project(filePath, this); CheckProject(mProject); } void MainWindow::CheckProject(Project *project) { if (!project->IsOpen()) { if (!project->Open()) { delete mProject; mProject = 0; return; } } QFileInfo inf(project->Filename()); const QString rootpath = project->GetProjectFile()->GetRootPath(); // 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 mCurrentDirectory = rootpath; QStringList paths = project->GetProjectFile()->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); } } DoCheckFiles(paths); } void MainWindow::NewProjectFile() { const QString filter = tr("Project files (*.cppcheck);;All files(*.*)"); QString filepath = QFileDialog::getSaveFileName(this, tr("Select Project Filename"), GetPath(SETTINGS_LAST_PROJECT_PATH), filter); if (filepath.isEmpty()) return; SetPath(SETTINGS_LAST_PROJECT_PATH, filepath); EnableProjectActions(true); QFileInfo inf(filepath); const QString filename = inf.fileName(); FormatAndSetTitle(tr("Project:") + QString(" ") + filename); delete mProject; mProject = new Project(filepath, this); mProject->Create(); if (mProject->Edit()) { AddProjectMRU(filepath); CheckProject(mProject); } } void MainWindow::CloseProjectFile() { delete mProject; mProject = NULL; EnableProjectActions(false); EnableProjectOpenActions(true); FormatAndSetTitle(); } void MainWindow::EditProjectFile() { if (!mProject) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), QString(tr("No project file loaded")), QMessageBox::Ok, this); msg.exec(); return; } mProject->Edit(); } void MainWindow::ShowLogView() { if (mLogView == NULL) mLogView = new LogView; mLogView->show(); if (!mLogView->isActiveWindow()) mLogView->activateWindow(); } void MainWindow::ShowStatistics() { StatsDialog statsDialog(this); // Show a dialog with the previous scan statistics and project information if (mProject) { statsDialog.setProject(*mProject); } statsDialog.setPathSelected(mCurrentDirectory); statsDialog.setNumberOfFilesScanned(mThread->GetPreviousFilesCount()); statsDialog.setScanDuration(mThread->GetPreviousScanDuration() / 1000.0); statsDialog.setStatistics(mUI.mResults->GetStatistics()); statsDialog.exec(); } void MainWindow::Log(const QString &logline) { if (mLogView) { mLogView->AppendLine(logline); } } void MainWindow::DebugError(const ErrorItem &item) { if (mLogView) { mLogView->AppendLine(item.ToString()); } } 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] != NULL) 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); } } cppcheck-1.66/gui/mainwindow.h000066400000000000000000000272501236713773000163560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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_main.h" class ThreadHandler; class TranslationHandler; class ScratchPad; class LogView; class Project; 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 Checks given code * * @param code Content of the (virtual) file to be checked * @param filename Name of the (virtual) file to be checked - determines language. */ void CheckCode(const QString& code, const QString& filename); public slots: /** * @brief Slot for check files menu item * */ void CheckFiles(); /** * @brief Slot to recheck files * */ void ReCheck(); /** * @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 check directory menu item * */ void CheckDirectory(); /** * @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 stop processing files * */ void Save(); /** * @brief Slot to create new project file * */ void NewProjectFile(); /** * @brief Slot to open project file and start checking contained paths. * */ void OpenProjectFile(); /** * @brief Slot to open project file and start checking contained paths. * */ void ShowScratchpad(); /** * @brief Slot to close open project file. * */ void CloseProjectFile(); /** * @brief Slot to edit project file. * */ void EditProjectFile(); /** * @brief Slot for showing the log view. * */ void ShowLogView(); /** * @brief Slot for showing the scan and project statistics. * */ void ShowStatistics(); protected slots: /** * @brief Slot for checkthread's done signal * */ void CheckDone(); /** * @brief Lock down UI while checking * */ 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 checking button is pressed * */ void StopChecking(); /** * @brief Open help file contents * */ void OpenHelpContents(); /** * @brief Add new line to log. * */ void Log(const QString &logline); /** * @brief Handle new debug error. * */ void DebugError(const ErrorItem &item); /** * @brief Filters the results in the result list. */ void FilterResults(); /** * @brief Opens recently opened project file. */ void OpenRecentProject(); /** * @brief Selects the platform as checked platform. */ void SelectPlatform(); private: /** * @brief Check the project. * @param project Pointer to the project to check. */ void CheckProject(Project *project); /** * @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 check. * Helper function to open a dialog to ask user to select files or * directory to check. 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 check */ QStringList SelectFilesToCheck(QFileDialog::FileMode mode); /** * @brief Check all files specified in parameter files * * @param files List of files and/or directories to check */ void DoCheckFiles(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 checking 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 checking. * @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 file Filename (inc. path) of XML file to load. */ void LoadResults(const QString file); /** * @brief Load XML file to the GUI. * @param file Filename (inc. path) of XML file to load. * @param checkedDirectory Path to the directory that the results were generated for. */ void LoadResults(const QString file, const QString checkedDirectory); /** * @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 True if successful */ Library::Error LoadLibrary(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 check 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 checked directory. */ QString mCurrentDirectory; /** * @brief Log view. */ LogView *mLogView; /** * @brief Scratchpad. */ ScratchPad* mScratchPad; /** * @brief Project (file). */ Project *mProject; /** * @brief Filter field in the Filter toolbar. */ QLineEdit* mLineEditFilter; /** * @brief Timer to delay filtering while typing. */ QTimer* mFilterTimer; /** * @brief GUI actions for selecting the checked platform. */ QActionGroup *mPlatformActions; /** * @brief GUI actions for selecting the coding standard. */ QActionGroup *mCStandardActions, *mCppStandardActions; /** * @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 Project MRU menu actions. * List of MRU menu actions. Needs also to store the separator. */ QAction *mRecentProjectActs[MaxRecentProjects + 1]; }; /// @} #endif // MAINWINDOW_H cppcheck-1.66/gui/platforms.cpp000066400000000000000000000033041236713773000165360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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("Built-in"), Settings::Unspecified); 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.66/gui/platforms.h000066400000000000000000000030361236713773000162050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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.66/gui/project.cpp000066400000000000000000000075401236713773000162030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "project.h" #include "projectfile.h" #include "projectfiledialog.h" Project::Project(QWidget *parent) : QObject(parent), mPFile(NULL), mParentWidget(parent) { } Project::Project(const QString &filename, QWidget *parent) : QObject(parent), mFilename(filename), mPFile(NULL), mParentWidget(parent) { } Project::~Project() { delete mPFile; } QString Project::Filename() const { return mFilename; } void Project::SetFilename(const QString &filename) { mFilename = filename; } bool Project::IsOpen() const { return mPFile != NULL; } bool Project::Open() { mPFile = new ProjectFile(mFilename, this); if (QFile::exists(mFilename)) { if (!mPFile->Read()) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Could not read the project file."), QMessageBox::Ok, mParentWidget); msg.exec(); mFilename = QString(); mPFile->SetFilename(mFilename); return false; } return true; } return false; } bool Project::Edit() { ProjectFileDialog dlg(mFilename, mParentWidget); QString root = mPFile->GetRootPath(); dlg.SetRootPath(root); QStringList includes = mPFile->GetIncludeDirs(); dlg.SetIncludepaths(includes); QStringList defines = mPFile->GetDefines(); dlg.SetDefines(defines); QStringList paths = mPFile->GetCheckPaths(); dlg.SetPaths(paths); QStringList ignorepaths = mPFile->GetExcludedPaths(); dlg.SetExcludedPaths(ignorepaths); QStringList libraries = mPFile->GetLibraries(); dlg.SetLibraries(libraries); QStringList suppressions = mPFile->GetSuppressions(); dlg.SetSuppressions(suppressions); int rv = dlg.exec(); if (rv == QDialog::Accepted) { QString root = dlg.GetRootPath(); mPFile->SetRootPath(root); QStringList includes = dlg.GetIncludePaths(); mPFile->SetIncludes(includes); QStringList defines = dlg.GetDefines(); mPFile->SetDefines(defines); QStringList paths = dlg.GetPaths(); mPFile->SetCheckPaths(paths); QStringList excludedpaths = dlg.GetExcludedPaths(); mPFile->SetExcludedPaths(excludedpaths); QStringList libraries = dlg.GetLibraries(); mPFile->SetLibraries(libraries); QStringList suppressions = dlg.GetSuppressions(); mPFile->SetSuppressions(suppressions); bool writeSuccess = mPFile->Write(); if (!writeSuccess) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Could not write the project file."), QMessageBox::Ok, mParentWidget); msg.exec(); } return writeSuccess; } return false; } void Project::Create() { mPFile = new ProjectFile(mFilename, this); } cppcheck-1.66/gui/project.h000066400000000000000000000043151236713773000156450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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_H #define PROJECT_H #include #include class QWidget; class ProjectFile; /// @addtogroup GUI /// @{ /** * @brief A class that contains project data and manages projects. * Currently only project file creation and editing is implemented. */ class Project : public QObject { Q_OBJECT public: Project(QWidget *parent = 0); Project(const QString &filename, QWidget *parent = 0); ~Project(); /** * @brief Return the filename of the project. * @return Project's filename. */ QString Filename() const; /** * @brief Set filename for the project file. * @param filename Filename. */ void SetFilename(const QString &filename); /** * @brief Is the project open? * The project is considered to be open if it has an opened project file. * @return true if the project is open, false otherwise. */ bool IsOpen() const; /** * @brief Open existing project file. */ bool Open(); /** * @brief Edit the project file. * @return true if editing was successful. */ bool Edit(); /** * @brief Create new project file. */ void Create(); /** * @brief Return current project file. * @return project file. */ ProjectFile * GetProjectFile() const { return mPFile; } private: QString mFilename; ProjectFile *mPFile; QWidget *mParentWidget; }; /// @} #endif // PROJECT_H cppcheck-1.66/gui/projectfile.cpp000066400000000000000000000402201236713773000170330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" static const char ProjectElementName[] = "project"; static const char ProjectVersionAttrib[] = "version"; static const char ProjectFileVersion[] = "1"; 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"; ProjectFile::ProjectFile(QObject *parent) : QObject(parent) { } ProjectFile::ProjectFile(const QString &filename, QObject *parent) : QObject(parent), mFilename(filename) { } bool ProjectFile::Read(const QString &filename) { if (!filename.isEmpty()) mFilename = filename; QFile file(mFilename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false; 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); // Find paths to check from inside project element if (insideProject && xmlReader.name() == PathsElementName) ReadCheckPaths(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); 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(); if (projectTagFound) return true; else return false; } QStringList ProjectFile::GetIncludeDirs() const { QStringList dirs; foreach(QString path, mIncludeDirs) { dirs << QDir::fromNativeSeparators(path); } return dirs; } QStringList ProjectFile::GetDefines() const { return mDefines; } QStringList ProjectFile::GetCheckPaths() const { QStringList paths; foreach(QString path, mPaths) { paths << QDir::fromNativeSeparators(path); } return paths; } QStringList ProjectFile::GetExcludedPaths() const { QStringList paths; foreach(QString path, mExcludedPaths) { paths << QDir::fromNativeSeparators(path); } return paths; } QStringList ProjectFile::GetLibraries() const { QStringList libraries; foreach(QString library, mLibraries) { libraries << library; } return libraries; } QStringList ProjectFile::GetSuppressions() const { QStringList suppressions; foreach(QString suppression, mSuppressions) { suppressions << suppression; } return suppressions; } void ProjectFile::ReadRootPath(QXmlStreamReader &reader) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value("", RootPathNameAttrib).toString(); if (!name.isEmpty()) mRootPath = name; } 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("", 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("", 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("", 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("", 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("", 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; } 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 (!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); 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(); } cppcheck-1.66/gui/projectfile.h000066400000000000000000000142641236713773000165110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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; } /** * @brief Get list of include directories. * @return list of directories. */ QStringList GetIncludeDirs() const; /** * @brief Get list of defines. * @return list of defines. */ QStringList GetDefines() const; /** * @brief Get list of paths to check. * @return list of paths. */ QStringList GetCheckPaths() const; /** * @brief Get list of paths to exclude from the check. * @return list of paths. */ QStringList GetExcludedPaths() const; /** * @brief Get list libraries. * @return list of libraries. */ QStringList GetLibraries() const; /** * @brief Get list suppressions. * @return list of suppressions. */ QStringList GetSuppressions() const; /** * @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; } /** * @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 Write project file (to disk). * @param filename Filename to use. */ bool Write(const QString &filename = QString()); /** * @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[]); /** * @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); /** * @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[]); private: /** * @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; /** * @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; }; /// @} #endif // PROJECT_FILE_H cppcheck-1.66/gui/projectfile.txt000066400000000000000000000035211236713773000170730ustar00rootroot00000000000000Project 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.66/gui/projectfile.ui000066400000000000000000000270571236713773000167030ustar00rootroot00000000000000 ProjectFile 0 0 467 329 Project File 0 Project Defines: mEditDefines Root: mEditProjectRoot Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. true Paths: Qt::Vertical 20 40 Add... Edit Remove Qt::Vertical 20 40 Includes Include directories: QAbstractItemView::SelectRows Add... Edit Remove Qt::Vertical 20 40 Up Down Exclude Paths: Add... Edit Remove Qt::Vertical 20 40 Suppressions Suppression list: Qt::Horizontal 40 20 Add Remove Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok mButtons tabWidget mEditDefines mEditProjectRoot mListPaths mBtnAddPath mBtnEditPath mBtnRemovePath mListIncludeDirs mBtnAddInclude mBtnEditInclude mBtnRemoveInclude mBtnIncludeUp mBtnIncludeDown mListExcludedPaths mBtnAddIgnorePath mBtnEditIgnorePath mBtnRemoveIgnorePath mButtons accepted() ProjectFile accept() 266 319 157 158 mButtons rejected() ProjectFile reject() 334 319 286 158 cppcheck-1.66/gui/projectfiledialog.cpp000066400000000000000000000320761236713773000202250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "common.h" #include "projectfiledialog.h" #include "library.h" #include "cppcheck.h" #include "errorlogger.h" ProjectFileDialog::ProjectFileDialog(const QString &path, QWidget *parent) : QDialog(parent) , mFilePath(path) { mUI.setupUi(this); const QFileInfo inf(path); QString filename = inf.fileName(); QString title = tr("Project file: %1").arg(filename); setWindowTitle(title); LoadSettings(); // Checkboxes for the libraries.. const QString applicationFilePath = QCoreApplication::applicationFilePath(); const QString appPath = QFileInfo(applicationFilePath).canonicalPath(); QSettings settings; const QString datadir = settings.value("DATADIR",QString()).toString(); QStringList searchPaths; searchPaths << appPath << appPath + "/cfg" << inf.canonicalPath(); 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.librariesLayout->addWidget(checkbox); mLibraryCheckboxes << checkbox; } connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); connect(mUI.mBtnAddInclude, SIGNAL(clicked()), this, SLOT(AddIncludeDir())); connect(mUI.mBtnAddPath, SIGNAL(clicked()), this, SLOT(AddPath())); connect(mUI.mBtnEditInclude, SIGNAL(clicked()), this, SLOT(EditIncludeDir())); connect(mUI.mBtnRemoveInclude, SIGNAL(clicked()), this, SLOT(RemoveIncludeDir())); connect(mUI.mBtnEditPath, SIGNAL(clicked()), this, SLOT(EditPath())); connect(mUI.mBtnRemovePath, SIGNAL(clicked()), this, SLOT(RemovePath())); connect(mUI.mBtnAddIgnorePath, SIGNAL(clicked()), this, SLOT(AddExcludePath())); connect(mUI.mBtnEditIgnorePath, SIGNAL(clicked()), this, SLOT(EditExcludePath())); connect(mUI.mBtnRemoveIgnorePath, SIGNAL(clicked()), this, SLOT(RemoveExcludePath())); connect(mUI.mBtnIncludeUp, SIGNAL(clicked()), this, SLOT(MoveIncludePathUp())); connect(mUI.mBtnIncludeDown, SIGNAL(clicked()), this, SLOT(MoveIncludePathDown())); connect(mUI.mBtnAddSuppression, SIGNAL(clicked()), this, SLOT(AddSuppression())); connect(mUI.mBtnRemoveSuppression, SIGNAL(clicked()), this, SLOT(RemoveSuppression())); } 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()); } 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::AddPath(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.mListPaths->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; } 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::GetPaths() const { const int count = mUI.mListPaths->count(); QStringList paths; for (int i = 0; i < count; i++) { QListWidgetItem *item = mUI.mListPaths->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) { QString newroot = QDir::toNativeSeparators(root); mUI.mEditProjectRoot->setText(newroot); } 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::SetPaths(const QStringList &paths) { foreach(QString path, paths) { AddPath(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::AddIncludeDir() { const QFileInfo inf(mFilePath); const QString rootpath = inf.absolutePath(); QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select include directory"), rootpath); if (!selectedDir.isEmpty()) { // 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(selectedDir); QString absPath = dir.absolutePath(); if (absPath.startsWith(rootpath)) { // Remove also the slash from begin of new relative path selectedDir = absPath.remove(0, rootpath.length() + 1); } if (!selectedDir.endsWith("/")) selectedDir += '/'; AddIncludeDir(selectedDir); } } void ProjectFileDialog::AddPath() { QFileInfo inf(mFilePath); const QString rootpath = inf.absolutePath(); QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select a directory to check"), rootpath); if (!selectedDir.isEmpty()) { AddPath(selectedDir); } } 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::EditPath() { QListWidgetItem *item = mUI.mListPaths->currentItem(); mUI.mListPaths->editItem(item); } void ProjectFileDialog::RemovePath() { const int row = mUI.mListPaths->currentRow(); QListWidgetItem *item = mUI.mListPaths->takeItem(row); delete item; } void ProjectFileDialog::AddExcludePath() { QFileInfo inf(mFilePath); const QString rootpath = inf.absolutePath(); QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select directory to ignore"), rootpath); if (!selectedDir.isEmpty()) { if (!selectedDir.endsWith('/')) selectedDir += '/'; AddExcludePath(selectedDir); } } 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.66/gui/projectfiledialog.h000066400000000000000000000126621236713773000176710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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_projectfile.h" class QWidget; /// @addtogroup GUI /// @{ /** * @brief A dialog for editing project file data. */ class ProjectFileDialog : public QDialog { Q_OBJECT public: ProjectFileDialog(const QString &path, QWidget *parent = 0); virtual ~ProjectFileDialog(); /** * @brief Return project root path from the dialog control. * @return Project root path. */ QString GetRootPath() 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 GetPaths() 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); /** * @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 SetPaths(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: /** * @brief Browse for include directory. * Allow user to add new include directory to the list. */ void AddIncludeDir(); /** * @brief Add new path to check. */ void AddPath(); /** * @brief Remove include directory from the list. */ void RemoveIncludeDir(); /** * @brief Edit include directory in the list. */ void EditIncludeDir(); /** * @brief Edit path in the list. */ void EditPath(); /** * @brief Remove path from the list. */ void RemovePath(); /** * @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 AddPath(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. */ QString mFilePath; /** @brief Library checkboxes */ QList mLibraryCheckboxes; }; /// @} #endif // PROJECTFILE_DIALOG_H cppcheck-1.66/gui/report.cpp000066400000000000000000000027361236713773000160520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "report.h" Report::Report(const QString &filename) : 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.66/gui/report.h000066400000000000000000000041371236713773000155140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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, XML, XMLV2, CSV, }; 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.66/gui/resultstree.cpp000066400000000000000000000776341236713773000171310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "common.h" #include "erroritem.h" #include "applicationlist.h" #include "resultstree.h" #include "report.h" #include "application.h" #include "showtypes.h" ResultsTree::ResultsTree(QWidget * parent) : QTreeView(parent), mContextItem(0), mShowErrorId(false), mVisibleErrors(false), mSelectionModel(0) { setModel(&mModel); Translate(); // Adds columns to grid setExpandsOnDoubleClick(false); setSortingEnabled(true); connect(this, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(QuickStartApplication(const QModelIndex &))); } ResultsTree::~ResultsTree() { } void ResultsTree::Initialize(QSettings *settings, ApplicationList *list) { mSettings = settings; mApplications = list; LoadSettings(); } QStandardItem *ResultsTree::CreateNormalItem(const QString &name) { QStandardItem *item = new QStandardItem(name); item->setData(name, Qt::ToolTipRole); item->setEditable(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.files.isEmpty()) { return false; } QString realfile = StripPath(item.files[0], false); if (realfile.isEmpty()) { realfile = tr("Undefined file"); } bool hide = !mShowSeverities.isShown(item.severity); //bool hide = !mShowTypes[SeverityToShowType(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.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.errorId = item.errorId; line.inconclusive = item.inconclusive; line.line = item.lines[0]; line.summary = item.summary; line.message = item.message; line.severity = item.severity; //Create the base item for the error and ensure it has a proper //file item as a parent QStandardItem *stditem = AddBacktraceFiles(EnsureFileItem(line.file, item.file0, hide), line, hide, SeverityToIcon(line.severity)); 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"] = item.files[0]; data["line"] = item.lines[0]; data["id"] = item.errorId; data["inconclusive"] = item.inconclusive; data["file0"] = item.file0; stditem->setData(QVariant(data)); //Add backtrace files as children for (int i = 1; i < item.files.size(); i++) { line.file = StripPath(item.files[i], false); line.line = item.lines[i]; QStandardItem *child_item; child_item = AddBacktraceFiles(stditem, line, hide, ":images/go-down.png"); //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"] = item.files[i]; child_data["line"] = line.line; child_data["id"] = line.errorId; child_data["inconclusive"] = line.inconclusive; child_item->setData(QVariant(child_data)); } //TODO just hide/show current error and it's file //since this does a lot of unnecessary work if (!hide) { ShowFileItem(realfile); } return true; } QStandardItem *ResultsTree::AddBacktraceFiles(QStandardItem *parent, const ErrorLine &item, const bool hide, const QString &icon) { if (!parent) { return 0; } QList list; // Ensure shown path is with native separators const QString file = QDir::toNativeSeparators(item.file); list << CreateNormalItem(file); const QString severity = SeverityToTranslatedString(item.severity); list << CreateNormalItem(severity); list << CreateLineNumberItem(QString("%1").arg(item.line)); list << CreateNormalItem(item.errorId); //TODO message has parameter names so we'll need changes to the core //cppcheck so we can get proper translations QString summary; if (item.inconclusive) { summary = tr("[Inconclusive]"); summary += " "; } summary += item.summary.toLatin1(); list << CreateNormalItem(summary); // 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 fourth column is the summary so check it last if (parent->child(i, 4)->text() == list[4]->text()) { // this row matches so don't add it return 0; } } } } 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 ""; } } 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 *item = mModel.item(i, 0); if (!item) continue; QVariantMap data = item->data().toMap(); if (stripped == data["file"].toString() || filename == data["file0"].toString()) { 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()); } 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::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 *file = mModel.item(i, 0); if (!file) continue; QVariantMap data = file->data().toMap(); data["hide"] = false; file->setData(QVariant(data)); int errorcount = file->rowCount(); for (int j = 0; j < errorcount; j++) { QStandardItem *child = file->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 *file = mModel.item(i, 0); if (!file) { continue; } //Get the amount of errors this file contains int errorcount = file->rowCount(); //By default it shouldn't be visible bool show = false; for (int j = 0; j < errorcount; j++) { //Get the error itself QStandardItem *child = file->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; } } if (!hide) { mVisibleErrors = true; } //Hide/show accordingly setRowHidden(j, file->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 (file->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::ShowFileItem(const QString &name) { QStandardItem *item = FindFileItem(name); if (item) { setRowHidden(0, mModel.indexFromItem(item), false); } } 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()) { //Go through all applications and add them to the context menu for (int i = 0; i < mApplications->GetApplicationCount(); i++) { //Create an action for the application const Application& app = mApplications->GetApplication(i); 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, i); } 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 *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); if (multipleSelection) { copyfilename->setDisabled(true); copypath->setDisabled(true); copymessage->setDisabled(true); copymessageid->setDisabled(true); hideallid->setDisabled(true); } menu.addAction(copyfilename); menu.addAction(copypath); menu.addAction(copymessage); menu.addAction(copymessageid); menu.addAction(hide); menu.addAction(hideallid); 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())); } //Start the menu menu.exec(e->globalPos()); 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_WS_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_WS_WIN if (program.indexOf(" ") > -1) { if (!program.startsWith('"') && !program.endsWith('"')) { program.insert(0, "\""); program.append("\""); } } #endif // Q_WS_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() { CopyPath(mContextItem, false); } void ResultsTree::CopyFullPath() { CopyPath(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(); QModelIndex index; foreach(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::HideAllIdResult() { 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(); // 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::Context(int application) { StartApplication(mContextItem, application); } void ResultsTree::QuickStartApplication(const QModelIndex &index) { StartApplication(mModel.itemFromIndex(index)); } void ResultsTree::CopyPath(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(); } QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(pathStr); } } 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 ""; } } void ResultsTree::SaveResults(Report *report) const { report->WriteHeader(); for (int i = 0; i < mModel.rowCount(); i++) { QStandardItem *item = mModel.item(i, 0); if (!isRowHidden(i, item->index())) SaveErrors(report, item); } report->WriteFooter(); } void ResultsTree::SaveErrors(Report *report, QStandardItem *item) const { if (!item) { return; } for (int i = 0; i < item->rowCount(); i++) { QStandardItem *error = item->child(i, 0); if (!error) { continue; } if (isRowHidden(i, item->index()) && !mSaveAllErrors) { continue; } //Get error's user data QVariant userdata = error->data(); //Convert it to QVariantMap QVariantMap data = userdata.toMap(); ErrorItem item; 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(); QString file = StripPath(data["file"].toString(), true); unsigned int line = data["line"].toUInt(); item.files << file; item.lines << line; for (int j = 0; j < error->rowCount(); j++) { 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(); file = StripPath(child_data["file"].toString(), true); line = child_data["line"].toUInt(); item.files << file; item.lines << line; } report->WriteError(item); } } void ResultsTree::UpdateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showErrorId) { if (mShowFullPath != showFullPath) { mShowFullPath = showFullPath; RefreshFilePaths(); } mSaveFullPath = saveFullPath; mSaveAllErrors = saveAllErrors; ShowIdColumn(showErrorId); } void ResultsTree::SetCheckDirectory(const QString &dir) { mCheckPath = dir; } 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("Summary"); 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::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QTreeView::currentChanged(current, previous); emit SelectionChanged(current); } cppcheck-1.66/gui/resultstree.h000066400000000000000000000264201236713773000165610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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; /// @addtogroup GUI /// @{ /** * @brief Cppcheck's results are shown in this tree * */ class ResultsTree : public QTreeView { Q_OBJECT public: ResultsTree(QWidget * parent = 0); virtual ~ResultsTree(); void Initialize(QSettings *settings, ApplicationList *list); /** * @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 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 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 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 */ void UpdateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showErrorId); /** * @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 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 Returns true if column "Id" is shown */ bool ShowIdColumn() const { return mShowErrorId; } /** * @brief GUI severities. */ ShowTypes mShowSeverities; 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 for selection change in result tree. * * @param current Model index to specify new selected item. */ void SelectionChanged(const QModelIndex ¤t); 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 context menu item to hide all messages with the current message Id * */ void HideAllIdResult(); /** * @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 item Item whose errors to save */ void SaveErrors(Report *report, QStandardItem *item) 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 CopyPath(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 * @return newly created QStandardItem * */ QStandardItem *AddBacktraceFiles(QStandardItem *parent, const ErrorLine &item, const bool hide, const QString &icon); /** * @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 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 Show a file item * * @param name Filename of the fileitem */ void ShowFileItem(const QString &name); /** * @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: QItemSelectionModel *mSelectionModel; }; /// @} #endif // RESULTSTREE_H cppcheck-1.66/gui/resultsview.cpp000066400000000000000000000222151236713773000171250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "common.h" #include "erroritem.h" #include "resultsview.h" #include "report.h" #include "txtreport.h" #include "xmlreport.h" #include "xmlreportv1.h" #include "xmlreportv2.h" #include "csvreport.h" #include "applicationlist.h" #include "checkstatistics.h" ResultsView::ResultsView(QWidget * parent) : QWidget(parent), mErrorsFound(false), mShowNoErrorsMessage(true), mStatistics(new CheckStatistics(this)) { mUI.setupUi(this); connect(mUI.mTree, SIGNAL(ResultsHidden(bool)), this, SIGNAL(ResultsHidden(bool))); connect(mUI.mTree, SIGNAL(SelectionChanged(const QModelIndex &)), this, SLOT(UpdateDetails(const QModelIndex &))); } void ResultsView::Initialize(QSettings *settings, ApplicationList *list) { 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); } ResultsView::~ResultsView() { //dtor } void ResultsView::Clear(bool results) { if (results) { mUI.mTree->Clear(); mErrorsFound = false; } mUI.mDetails->setText(""); 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); /** * @todo Optimize this.. It is inefficient to check this every time. */ // If the results list got empty.. if (!mUI.mTree->HasResults()) mErrorsFound = false; } 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) { mErrorsFound = true; if (mUI.mTree->AddErrorItem(item)) { emit GotResults(); mStatistics->AddItem(ShowTypes::SeverityToShowType(item.severity)); } } void ResultsView::ShowResults(ShowTypes::ShowType type, bool show) { mUI.mTree->ShowResults(type, show); } void ResultsView::CollapseAllResults() { mUI.mTree->collapseAll(); } void ResultsView::ExpandAllResults() { mUI.mTree->expandAll(); } void ResultsView::ShowHiddenResults() { mUI.mTree->ShowHiddenResults(); } void ResultsView::FilterResults(const QString& filter) { mUI.mTree->FilterResults(filter); } void ResultsView::Save(const QString &filename, Report::Type type) const { if (!mErrorsFound) { 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::XML: report = new XmlReportV1(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::UpdateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showNoErrorsMessage, bool showErrorId) { mUI.mTree->UpdateSettings(showFullPath, saveFullPath, saveAllErrors, showErrorId); mShowNoErrorsMessage = showNoErrorsMessage; } void ResultsView::SetCheckDirectory(const QString &dir) { mUI.mTree->SetCheckDirectory(dir); } 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 (!mErrorsFound) { 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; } XmlReport *report = NULL; if (version == 1) report = new XmlReportV1(filename); else if (version == 2) 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(""); } void ResultsView::UpdateDetails(const QModelIndex &index) { QStandardItemModel *model = qobject_cast(mUI.mTree->model()); QStandardItem *item = model->itemFromIndex(index); if (!item) { mUI.mDetails->setText(""); 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(""); 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); if (mUI.mTree->ShowIdColumn()) formattedMsg.prepend(tr("Id") + ": " + data["id"].toString() + "\n"); mUI.mDetails->setText(formattedMsg); } cppcheck-1.66/gui/resultsview.h000066400000000000000000000133441236713773000165750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 QSettings; class CheckStatistics; /// @addtogroup GUI /// @{ /** * @brief Widget to show cppcheck progressbar and result * */ class ResultsView : public QWidget { Q_OBJECT public: ResultsView(QWidget * parent = 0); void Initialize(QSettings *settings, ApplicationList *list); virtual ~ResultsView(); /** * @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 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 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 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? */ void UpdateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showNoErrorsMessage, bool showErrorId); /** * @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 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); 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 Collapse all results in the result list. */ void CollapseAllResults(); /** * @brief Expand all results in the result list. */ void ExpandAllResults(); /** * @brief Filters the results in the result list. */ void FilterResults(const QString& filter); /** * @brief Show hidden results in the result list. */ void ShowHiddenResults(); /** * @brief Update detailed message when selected item is changed. * * @param index Position of new selected item. */ void UpdateDetails(const QModelIndex &index); protected: /** * @brief Have any errors been found */ bool mErrorsFound; /** * @brief Should we show a "No errors found dialog" every time no errors were found? */ bool mShowNoErrorsMessage; Ui::ResultsView mUI; CheckStatistics *mStatistics; private: }; /// @} #endif // RESULTSVIEW_H cppcheck-1.66/gui/resultsview.ui000066400000000000000000000042511236713773000167600ustar00rootroot00000000000000 ResultsView 0 0 459 357 0 0 16777215 16777215 Results 0 0 0 24 Qt::Vertical 0 0 QAbstractItemView::ExtendedSelection false true ResultsTree QTreeView
resultstree.h
mTree mDetails
cppcheck-1.66/gui/scratchpad.cpp000066400000000000000000000023731236713773000166500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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, SIGNAL(clicked()), this, SLOT(CheckButtonClicked())); } void ScratchPad::CheckButtonClicked() { QString filename = mUI.lineEdit->text(); if (filename.isEmpty()) filename = "test.cpp"; mMainWindow.CheckCode(mUI.plainTextEdit->toPlainText(), filename); } cppcheck-1.66/gui/scratchpad.h000066400000000000000000000023631236713773000163140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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.66/gui/scratchpad.ui000066400000000000000000000046541236713773000165070ustar00rootroot00000000000000 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.66/gui/settings.ui000066400000000000000000000307201236713773000162240ustar00rootroot00000000000000 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 Qt::Vertical 20 40 Paths Include paths: QAbstractItemView::SelectRows Add... Edit Remove Qt::Vertical 20 40 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 Advanced &Show inconclusive errors S&how internal warnings in log Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok tabWidget mJobs mForce mShowFullPath mShowNoErrorsMessage mInlineSuppressions mListIncludePaths mBtnAddIncludePath mBtnEditIncludePath mBtnRemoveIncludePath mListWidget mBtnAddApplication mBtnEditApplication mBtnRemoveApplication mBtnDefaultApplication mSaveAllErrors mSaveFullPath mListLanguages mEnableInconclusive mShowDebugWarnings mButtons mButtons accepted() Settings accept() 248 254 157 274 mButtons rejected() Settings reject() 316 260 286 274 cppcheck-1.66/gui/settingsdialog.cpp000066400000000000000000000263271236713773000175610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.mShowErrorId->setCheckState(BoolToCheckState(settings.value(SETTINGS_SHOW_ERROR_ID, false).toBool())); connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(Ok())); connect(mUI.mButtons, SIGNAL(rejected()), this, SLOT(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.mBtnAddIncludePath, SIGNAL(clicked()), this, SLOT(AddIncludePath())); connect(mUI.mBtnRemoveIncludePath, SIGNAL(clicked()), this, SLOT(RemoveIncludePath())); connect(mUI.mBtnEditIncludePath, SIGNAL(clicked()), this, SLOT(EditIncludePath())); 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(); InitIncludepathsList(); } SettingsDialog::~SettingsDialog() { SaveSettings(); } void SettingsDialog::AddIncludePath(const QString &path) { if (path.isNull() || path.isEmpty()) return; QListWidgetItem *item = new QListWidgetItem(path); item->setFlags(item->flags() | Qt::ItemIsEditable); mUI.mListIncludePaths->addItem(item); } void SettingsDialog::InitIncludepathsList() { QSettings settings; const QString allPaths = settings.value(SETTINGS_GLOBAL_INCLUDE_PATHS).toString(); const QStringList paths = allPaths.split(";", QString::SkipEmptyParts); foreach(QString path, paths) { AddIncludePath(path); } } 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) 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.mShowErrorId, SETTINGS_SHOW_ERROR_ID); const QListWidgetItem *currentLang = mUI.mListLanguages->currentItem(); if (currentLang) { const QString langcode = currentLang->data(LangCodeRole).toString(); settings.setValue(SETTINGS_LANGUAGE, langcode); } const int count = mUI.mListIncludePaths->count(); QString includePaths; for (int i = 0; i < count; i++) { QListWidgetItem *item = mUI.mListIncludePaths->item(i); includePaths += item->text(); includePaths += ";"; } settings.setValue(SETTINGS_GLOBAL_INCLUDE_PATHS, includePaths); } 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) { item->setText(app.getName()); } } } 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()); } void SettingsDialog::AddIncludePath() { QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select include directory"), GetPath(SETTINGS_LAST_INCLUDE_PATH)); if (!selectedDir.isEmpty()) { AddIncludePath(selectedDir); SetPath(SETTINGS_LAST_INCLUDE_PATH, selectedDir); } } void SettingsDialog::RemoveIncludePath() { const int row = mUI.mListIncludePaths->currentRow(); QListWidgetItem *item = mUI.mListIncludePaths->takeItem(row); delete item; } void SettingsDialog::EditIncludePath() { QListWidgetItem *item = mUI.mListIncludePaths->currentItem(); mUI.mListIncludePaths->editItem(item); } cppcheck-1.66/gui/settingsdialog.h000066400000000000000000000116251236713773000172210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 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 adding new include path * */ void AddIncludePath(); /** * @brief Slot for removing an include path. * */ void RemoveIncludePath(); /** * @brief Slot for editing an include path. * */ void EditIncludePath(); protected: /** * @brief Add new include path to the list. * @param path Path to add. * */ void AddIncludePath(const QString &path); /** * @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 SaveSettings() const; /** * @brief Save settings * Save dialog size and column widths. * */ void LoadSettings(); /** * @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 include paths-list. */ void InitIncludepathsList(); /** * @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.66/gui/showtypes.cpp000066400000000000000000000074141236713773000166020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/showtypes.h000066400000000000000000000066751236713773000162570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/stats.ui000066400000000000000000000303351236713773000155240ustar00rootroot00000000000000 StatsDialog 0 0 502 272 Statistics QTabWidget::Rounded 2 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 0 Qt::Horizontal 40 20 Copy to Clipboard 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.66/gui/statsdialog.cpp000066400000000000000000000262731236713773000170570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "project.h" #include "projectfile.h" #include "statsdialog.h" #include "checkstatistics.h" StatsDialog::StatsDialog(QWidget *parent) : QDialog(parent) { mUI.setupUi(this); connect(mUI.mCopyToClipboard, SIGNAL(pressed()), this, SLOT(copyToClipboard())); } void StatsDialog::setProject(const Project& project) { ProjectFile *projectFile = project.GetProjectFile(); 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(";")); } else { mUI.mProject->setText(""); mUI.mPaths->setText(""); mUI.mIncludePaths->setText(""); mUI.mDefines->setText(""); } } 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::copyToClipboard() { QClipboard *clipboard = QApplication::clipboard(); if (clipboard) { 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(ShowTypes::ShowErrors)) .arg(warnings) .arg(mStatistics->GetCount(ShowTypes::ShowWarnings)) .arg(style) .arg(mStatistics->GetCount(ShowTypes::ShowStyle)) .arg(portability) .arg(mStatistics->GetCount(ShowTypes::ShowPortability)) .arg(performance) .arg(mStatistics->GetCount(ShowTypes::ShowPerformance)) .arg(information) .arg(mStatistics->GetCount(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(ShowTypes::ShowErrors)) .arg(warnings) .arg(mStatistics->GetCount(ShowTypes::ShowWarnings)) .arg(style) .arg(mStatistics->GetCount(ShowTypes::ShowStyle)) .arg(portability) .arg(mStatistics->GetCount(ShowTypes::ShowPortability)) .arg(performance) .arg(mStatistics->GetCount(ShowTypes::ShowPerformance)) .arg(information) .arg(mStatistics->GetCount(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 = const_cast(stats); mUI.mLblErrors->setText(QString("%1").arg(stats->GetCount(ShowTypes::ShowErrors))); mUI.mLblWarnings->setText(QString("%1").arg(stats->GetCount(ShowTypes::ShowWarnings))); mUI.mLblStyle->setText(QString("%1").arg(stats->GetCount(ShowTypes::ShowStyle))); mUI.mLblPortability->setText(QString("%1").arg(stats->GetCount(ShowTypes::ShowPortability))); mUI.mLblPerformance->setText(QString("%1").arg(stats->GetCount(ShowTypes::ShowPerformance))); mUI.mLblInformation->setText(QString("%1").arg(stats->GetCount(ShowTypes::ShowInformation))); } cppcheck-1.66/gui/statsdialog.h000066400000000000000000000035361236713773000165210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include "ui_stats.h" class Project; class CheckStatistics; /// @addtogroup GUI /// @{ /** * @brief A dialog that shows project and scan statistics. * */ class StatsDialog : public QDialog { Q_OBJECT public: StatsDialog(QWidget *parent = 0); /** * @brief Sets the project to extract statistics from */ void setProject(const Project& project); /** * @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(); private: Ui::StatsDialog mUI; CheckStatistics *mStatistics; }; /// @} #endif // STATSDIALOG_H cppcheck-1.66/gui/test/000077500000000000000000000000001236713773000150025ustar00rootroot00000000000000cppcheck-1.66/gui/test/benchmark/000077500000000000000000000000001236713773000167345ustar00rootroot00000000000000cppcheck-1.66/gui/test/benchmark/benchmark.pro000066400000000000000000000000701236713773000214050ustar00rootroot00000000000000CONFIG += ordered TEMPLATE = subdirs SUBDIRS = simple cppcheck-1.66/gui/test/benchmark/common.pri000066400000000000000000000003311236713773000207350ustar00rootroot00000000000000CONFIG += qtestlib #DEPENDPATH += . .. INCLUDEPATH += . ../.. ../../../../lib LIBS += -L../../../../externals -lpcre INCLUDEPATH += ../../../externals BASEPATH = ../../../../lib/ include($$PWD/../../../lib/lib.pri) cppcheck-1.66/gui/test/benchmark/simple/000077500000000000000000000000001236713773000202255ustar00rootroot00000000000000cppcheck-1.66/gui/test/benchmark/simple/benchmarksimple.cpp000066400000000000000000000043301236713773000240750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.simplifyTokenList(); } } 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.simplifyTokenList(); } } QTEST_MAIN(BenchmarkSimple) cppcheck-1.66/gui/test/benchmark/simple/benchmarksimple.h000066400000000000000000000023371236713773000235470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/benchmark/simple/simple.pro000066400000000000000000000003711236713773000222410ustar00rootroot00000000000000TEMPLATE = 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.66/gui/test/common.pri000066400000000000000000000012021236713773000170010ustar00rootroot00000000000000CONFIG += qtestlib #DEPENDPATH += . .. INCLUDEPATH += . ../.. ../../../lib LIBS += -L../../../externals -lpcre INCLUDEPATH += ../../externals BASEPATH = ../../../lib/ include($$PWD/../../lib/lib.pri) # GUI SOURCES += ../../erroritem.cpp \ ../../filelist.cpp \ ../../projectfile.cpp \ ../../report.cpp \ ../../translationhandler.cpp \ ../../xmlreport.cpp \ ../../xmlreportv1.cpp \ ../../xmlreportv2.cpp HEADERS += ../../erroritem.h \ ../../filelist.h \ ../../projectfile.h \ ../../report.h \ ../../translationhandler.h \ ../../xmlreport.h \ ../../xmlreportv1.h \ ../../xmlreportv2.h cppcheck-1.66/gui/test/data/000077500000000000000000000000001236713773000157135ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/benchmark/000077500000000000000000000000001236713773000176455ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/benchmark/simple.cpp000066400000000000000000004105651236713773000216550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 dont 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 == false && _write == false); } 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.66/gui/test/data/files/000077500000000000000000000000001236713773000170155ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/files/bar1000066400000000000000000000000211236713773000175560ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/bar1.foo000066400000000000000000000000211236713773000203400ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/dir1/000077500000000000000000000000001236713773000176545ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/files/dir1/dir11/000077500000000000000000000000001236713773000205745ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/files/dir1/dir11/foo11.cpp000066400000000000000000000000211236713773000222160ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/dir1/foo1.cpp000066400000000000000000000000211236713773000212150ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/dir2/000077500000000000000000000000001236713773000176555ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/files/dir2/foo1.cpp000066400000000000000000000000211236713773000212160ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo1.cpp000066400000000000000000000000211236713773000203560ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo2.cxx000066400000000000000000000000211236713773000203770ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo3.cc000066400000000000000000000000211236713773000201630ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo4.c000066400000000000000000000000211236713773000200210ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo5.c++000066400000000000000000000000211236713773000201500ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo6.txx000066400000000000000000000000211236713773000204240ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/files/foo7.tpp000066400000000000000000000000211236713773000204050ustar00rootroot00000000000000Dummy test file. cppcheck-1.66/gui/test/data/projectfiles/000077500000000000000000000000001236713773000204045ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/projectfiles/simple.cppcheck000066400000000000000000000006031236713773000233760ustar00rootroot00000000000000 cppcheck-1.66/gui/test/data/projectfiles/simple_ignore.cppcheck000066400000000000000000000006011236713773000247370ustar00rootroot00000000000000 cppcheck-1.66/gui/test/data/projectfiles/simple_noroot.cppcheck000066400000000000000000000005501236713773000247770ustar00rootroot00000000000000 cppcheck-1.66/gui/test/data/xmlfiles/000077500000000000000000000000001236713773000175365ustar00rootroot00000000000000cppcheck-1.66/gui/test/data/xmlfiles/xmlreport_v1.xml000066400000000000000000000023231236713773000227220ustar00rootroot00000000000000 cppcheck-1.66/gui/test/data/xmlfiles/xmlreport_v2.xml000066400000000000000000000040061236713773000227230ustar00rootroot00000000000000 cppcheck-1.66/gui/test/filelist/000077500000000000000000000000001236713773000166155ustar00rootroot00000000000000cppcheck-1.66/gui/test/filelist/filelist.pro000066400000000000000000000003521236713773000211520ustar00rootroot00000000000000TEMPLATE = 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.66/gui/test/filelist/testfilelist.cpp000066400000000000000000000142671236713773000220460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/filelist/testfilelist.h000066400000000000000000000021641236713773000215040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/projectfile/000077500000000000000000000000001236713773000173105ustar00rootroot00000000000000cppcheck-1.66/gui/test/projectfile/projectfile.pro000066400000000000000000000003631236713773000223420ustar00rootroot00000000000000TEMPLATE = 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.66/gui/test/projectfile/testprojectfile.cpp000066400000000000000000000071171236713773000232300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/projectfile/testprojectfile.h000066400000000000000000000017431236713773000226740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/readme.txt000066400000000000000000000013071236713773000170010ustar00rootroot00000000000000GUI 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.66/gui/test/test.pro000066400000000000000000000002511236713773000165010ustar00rootroot00000000000000CONFIG += ordered TEMPLATE = subdirs SUBDIRS = benchmark \ filelist \ projectfile \ translationhandler \ xmlreport \ xmlreportv1 \ xmlreportv2 cppcheck-1.66/gui/test/translationhandler/000077500000000000000000000000001236713773000206765ustar00rootroot00000000000000cppcheck-1.66/gui/test/translationhandler/testtranslationhandler.cpp000066400000000000000000000021461236713773000262010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/translationhandler/testtranslationhandler.h000066400000000000000000000016201236713773000256420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/translationhandler/translationhandler.pro000066400000000000000000000003471236713773000253200ustar00rootroot00000000000000TEMPLATE = app TARGET = test-translationhandler DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) # tests SOURCES += testtranslationhandler.cpp HEADERS += testtranslationhandler.h cppcheck-1.66/gui/test/xmlreport/000077500000000000000000000000001236713773000170365ustar00rootroot00000000000000cppcheck-1.66/gui/test/xmlreport/testxmlreport.cpp000066400000000000000000000032351236713773000225010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/xmlreport/testxmlreport.h000066400000000000000000000017451236713773000221520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/xmlreport/xmlreport.pro000066400000000000000000000003551236713773000216170ustar00rootroot00000000000000TEMPLATE = 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.66/gui/test/xmlreportv1/000077500000000000000000000000001236713773000173055ustar00rootroot00000000000000cppcheck-1.66/gui/test/xmlreportv1/testxmlreportv1.cpp000066400000000000000000000031441236713773000232160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testxmlreportv1.h" #include "xmlreportv1.h" #include "erroritem.h" #include "errorlogger.h" void TestXmlReportV1::readXml() { const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v1.xml"); XmlReportV1 report(filepath); QVERIFY(report.Open()); QList errors = report.Read(); QCOMPARE(errors.size(), 6); ErrorItem item = errors[0]; QCOMPARE(item.file, QString("test.cxx")); QCOMPARE(item.lines[0], (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")); } QTEST_MAIN(TestXmlReportV1) cppcheck-1.66/gui/test/xmlreportv1/testxmlreportv1.h000066400000000000000000000016071236713773000226650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 TestXmlReportV1: public QObject { Q_OBJECT private slots: void readXml(); }; cppcheck-1.66/gui/test/xmlreportv1/xmlreportv1.pro000066400000000000000000000003631236713773000223340ustar00rootroot00000000000000TEMPLATE = app TARGET = test-xmlreportv1 DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testxmlreportv1.cpp HEADERS += testxmlreportv1.h cppcheck-1.66/gui/test/xmlreportv2/000077500000000000000000000000001236713773000173065ustar00rootroot00000000000000cppcheck-1.66/gui/test/xmlreportv2/testxmlreportv2.cpp000066400000000000000000000045541236713773000232260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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); ErrorItem item = errors[0]; QCOMPARE(item.file, QString("test.cxx")); QCOMPARE(item.files.size(), 1); QCOMPARE(item.lines.size(), 1); QCOMPARE(item.files[0], QString("test.cxx")); QCOMPARE(item.lines[0], (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")); ErrorItem item2 = errors[3]; QCOMPARE(item2.file, QString("test.cxx")); QCOMPARE(item2.files.size(), 2); QCOMPARE(item2.lines.size(), 2); QCOMPARE(item2.files[0], QString("test.cxx")); QCOMPARE(item2.lines[0], (unsigned int)32); QCOMPARE(item2.files[1], QString("test.cxx")); QCOMPARE(item2.lines[1], (unsigned int)16); 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.66/gui/test/xmlreportv2/testxmlreportv2.h000066400000000000000000000016071236713773000226670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.66/gui/test/xmlreportv2/xmlreportv2.pro000066400000000000000000000003631236713773000223360ustar00rootroot00000000000000TEMPLATE = 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.66/gui/threadhandler.cpp000066400000000000000000000143341236713773000173410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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) { SetThreadCount(1); } ThreadHandler::~ThreadHandler() { RemoveThreads(); } void ThreadHandler::ClearFiles() { mLastFiles.clear(); mResults.ClearFiles(); } void ThreadHandler::SetFiles(const QStringList &files) { mResults.SetFiles(files); mLastFiles = files; } void ThreadHandler::Check(const Settings &settings, bool recheck) { if (recheck && mRunningThreadCount == 0) { // only recheck changed files mResults.SetFiles(GetReCheckFiles()); } 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]->Check(settings); } // Date and time when checking starts.. mCheckStartTime = QDateTime::currentDateTime(); 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(), SIGNAL(Done()), this, SLOT(ThreadDone())); connect(mThreads.last(), SIGNAL(FileChecked(const QString &)), &mResults, SLOT(FileChecked(const QString &))); } } void ThreadHandler::RemoveThreads() { for (int i = 0; i < mThreads.size(); i++) { mThreads[i]->terminate(); disconnect(mThreads.last(), SIGNAL(Done()), this, SLOT(ThreadDone())); disconnect(mThreads.last(), SIGNAL(FileChecked(const QString &)), &mResults, SLOT(FileChecked(const QString &))); delete mThreads[i]; } mThreads.clear(); } void ThreadHandler::ThreadDone() { 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(); for (int i = 0; i < mThreads.size(); i++) { mThreads[i]->stop(); } } void ThreadHandler::Initialize(ResultsView *view) { connect(&mResults, SIGNAL(Progress(int, const QString&)), view, SLOT(Progress(int, const QString&))); connect(&mResults, SIGNAL(Error(const ErrorItem &)), view, SLOT(Error(const ErrorItem &))); connect(&mResults, SIGNAL(Log(const QString &)), parent(), SLOT(Log(const QString &))); connect(&mResults, SIGNAL(DebugError(const ErrorItem &)), parent(), SLOT(DebugError(const ErrorItem &))); } 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() const { if (mLastCheckTime.isNull()) 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; } cppcheck-1.66/gui/threadhandler.h000066400000000000000000000111001236713773000167720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" 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: 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; /** * @brief Clear all files from cppcheck * */ void ClearFiles(); /** * @brief Set files to check * * @param files files to check */ void SetFiles(const QStringList &files); /** * @brief Start the threads to check the files * * @param settings Settings for checking * @param recheck Should we reuse the files we checked earlier */ void Check(const Settings &settings, bool recheck); /** * @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() const; signals: /** * @brief Signal that all threads are done * */ void Done(); 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; 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.66/gui/threadresult.cpp000066400000000000000000000066521236713773000172460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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); QList lines; QStringList files; for (std::list::const_iterator tok = msg._callStack.begin(); tok != msg._callStack.end(); ++tok) { files << QString((*tok).getfile(false).c_str()); lines << (*tok).line; } ErrorItem item; item.file = QString::fromStdString(callStackToString(msg._callStack)); item.files = files; item.errorId = QString::fromStdString(msg._id); item.lines = lines; item.summary = QString::fromStdString(msg.shortMessage()); item.message = QString::fromStdString(msg.verboseMessage()); item.severity = msg._severity; item.inconclusive = msg._inconclusive; item.file0 = QString::fromStdString(msg.file0); if (msg._severity != Severity::debug) emit Error(item); else emit DebugError(item); } QString ThreadResult::GetNextFile() { QMutexLocker locker(&mutex); if (mFiles.isEmpty()) { return ""; } return mFiles.takeFirst(); } 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::ClearFiles() { QMutexLocker locker(&mutex); mFiles.clear(); mFilesChecked = 0; mTotalFiles = 0; } int ThreadResult::GetFileCount() const { QMutexLocker locker(&mutex); return mFiles.size(); } cppcheck-1.66/gui/threadresult.h000066400000000000000000000057771236713773000167220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" 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(); /** * @brief Set list of files to check * @param files List of files to check */ void SetFiles(const QStringList &files); /** * @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; /** * @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.66/gui/translationhandler.cpp000066400000000000000000000152471236713773000204340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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() { QT_TRANSLATE_NOOP("QDialogButtonBox", "OK"); QT_TRANSLATE_NOOP("QDialogButtonBox", "Cancel"); QT_TRANSLATE_NOOP("QDialogButtonBox", "Close"); QT_TRANSLATE_NOOP("QDialogButtonBox", "Save"); } TranslationHandler::TranslationHandler(QObject *parent) : QObject(parent), mCurrentLanguage("en"), mTranslator(NULL) { // Add our available languages // Keep this list sorted AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Chinese (Simplified)"), "cppcheck_zh_CN"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Dutch"), "cppcheck_nl"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "English"), "cppcheck_en"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Finnish"), "cppcheck_fi"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "French"), "cppcheck_fr"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "German"), "cppcheck_de"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Italian"), "cppcheck_it"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Japanese"), "cppcheck_ja"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Korean"), "cppcheck_ko"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Russian"), "cppcheck_ru"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Serbian"), "cppcheck_sr"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "Spanish"), "cppcheck_es"); AddTranslation(QT_TRANSLATE_NOOP("MainWindow", "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) { translationFile += ".qm"; //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: 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.66/gui/txtreport.cpp000066400000000000000000000040311236713773000166000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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.lines.size(); i++) { const QString file = QDir::toNativeSeparators(error.files[i]); line += QString("[%1:%2]").arg(file).arg(error.lines[i]); if (i < error.lines.size() - 1 && !error.lines.isEmpty()) { line += " -> "; } if (i == error.lines.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.66/gui/txtreport.h000066400000000000000000000033071236713773000162520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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.66/gui/xmlreport.cpp000066400000000000000000000056771236713773000166020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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("", 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.66/gui/xmlreport.h000066400000000000000000000034451236713773000162360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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.66/gui/xmlreportv1.cpp000066400000000000000000000135251236713773000170400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "xmlreportv1.h" static const char ResultElementName[] = "results"; static const char ErrorElementName[] = "error"; static const char FilenameAttribute[] = "file"; static const char LineAttribute[] = "line"; static const char IdAttribute[] = "id"; static const char SeverityAttribute[] = "severity"; static const char MsgAttribute[] = "msg"; XmlReportV1::XmlReportV1(const QString &filename) : XmlReport(filename), mXmlReader(NULL), mXmlWriter(NULL) { } XmlReportV1::~XmlReportV1() { delete mXmlReader; delete mXmlWriter; } bool XmlReportV1::Create() { if (Report::Create()) { mXmlWriter = new QXmlStreamWriter(Report::GetFile()); return true; } return false; } bool XmlReportV1::Open() { if (Report::Open()) { mXmlReader = new QXmlStreamReader(Report::GetFile()); return true; } return false; } void XmlReportV1::WriteHeader() { mXmlWriter->setAutoFormatting(true); mXmlWriter->writeStartDocument(); mXmlWriter->writeStartElement(ResultElementName); } void XmlReportV1::WriteFooter() { mXmlWriter->writeEndElement(); mXmlWriter->writeEndDocument(); } void XmlReportV1::WriteError(const ErrorItem &error) { /* Error example from the core program in xml The callstack seems to be ignored here as well, instead last item of the stack is used */ // Don't write inconclusive errors to XML V1 if (error.inconclusive) return; mXmlWriter->writeStartElement(ErrorElementName); QString file = QDir::toNativeSeparators(error.files[error.files.size() - 1]); file = XmlReport::quoteMessage(file); mXmlWriter->writeAttribute(FilenameAttribute, file); const QString line = QString::number(error.lines[error.lines.size() - 1]); mXmlWriter->writeAttribute(LineAttribute, line); mXmlWriter->writeAttribute(IdAttribute, error.errorId); // Don't localize severity so we can read these files mXmlWriter->writeAttribute(SeverityAttribute, GuiSeverity::toString(error.severity)); const QString message = XmlReport::quoteMessage(error.message); mXmlWriter->writeAttribute(MsgAttribute, message); mXmlWriter->writeEndElement(); } QList XmlReportV1::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 XmlReportV1::ReadError(QXmlStreamReader *reader) { ErrorItem item; if (reader->name().toString() == ErrorElementName) { QXmlStreamAttributes attribs = reader->attributes(); QString file = attribs.value("", FilenameAttribute).toString(); file = XmlReport::unquoteMessage(file); item.file = file; item.files.push_back(file); const int line = attribs.value("", LineAttribute).toString().toUInt(); item.lines.push_back(line); item.errorId = attribs.value("", IdAttribute).toString(); item.severity = GuiSeverity::fromString(attribs.value("", SeverityAttribute).toString()); // NOTE: This duplicates the message to Summary-field. But since // old XML format doesn't have separate summary and verbose messages // we must add same message to both data so it shows up in GUI. // Check if there is full stop and cut the summary to it. QString summary = attribs.value("", MsgAttribute).toString(); const int ind = summary.indexOf('.'); if (ind != -1) summary = summary.left(ind + 1); item.summary = XmlReport::unquoteMessage(summary); QString message = attribs.value("", MsgAttribute).toString(); item.message = XmlReport::unquoteMessage(message); } return item; } cppcheck-1.66/gui/xmlreportv1.h000066400000000000000000000044101236713773000164760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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_REPORTV1_H #define XML_REPORTV1_H #include #include #include #include "xmlreport.h" /// @addtogroup GUI /// @{ /** * @brief XML file report version 1. * This report outputs XML-formatted report, version 1. The XML format must match command * line version's XML output. */ class XmlReportV1 : public XmlReport { public: XmlReportV1(const QString &filename); virtual ~XmlReportV1(); /** * @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_REPORTV1_H cppcheck-1.66/gui/xmlreportv2.cpp000066400000000000000000000176151236713773000170450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 char ResultElementName[] = "results"; static const char CppcheckElementName[] = "cppcheck"; static const char ErrorElementName[] = "error"; static const char ErrorsElementName[] = "errors"; static const char LocationElementName[] = "location"; static const char FilenameAttribute[] = "file"; static const char LineAttribute[] = "line"; static const char IdAttribute[] = "id"; static const char SeverityAttribute[] = "severity"; static const char MsgAttribute[] = "msg"; static const char VersionAttribute[] = "version"; static const char 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 */ // Don't write inconclusive errors to XML V2 until we decide the format if (error.inconclusive) return; 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); for (int i = 0; i < error.files.count(); i++) { mXmlWriter->writeStartElement(LocationElementName); QString file = QDir::toNativeSeparators(error.files[i]); file = XmlReport::quoteMessage(file); mXmlWriter->writeAttribute(FilenameAttribute, file); const QString line = QString::number(error.lines[i]); mXmlWriter->writeAttribute(LineAttribute, line); 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("", IdAttribute).toString(); item.severity = GuiSeverity::fromString(attribs.value("", SeverityAttribute).toString()); const QString summary = attribs.value("", MsgAttribute).toString(); item.summary = XmlReport::unquoteMessage(summary); const QString message = attribs.value("", VerboseAttribute).toString(); item.message = XmlReport::unquoteMessage(message); } 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 file = attribs.value("", FilenameAttribute).toString(); file = XmlReport::unquoteMessage(file); if (item.file.isEmpty()) item.file = file; item.files.push_back(file); const int line = attribs.value("", LineAttribute).toString().toUInt(); item.lines.push_back(line); } 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; } } return item; } cppcheck-1.66/gui/xmlreportv2.h000066400000000000000000000044201236713773000165000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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.66/htmlreport/000077500000000000000000000000001236713773000154375ustar00rootroot00000000000000cppcheck-1.66/htmlreport/README.txt000066400000000000000000000006641236713773000171430ustar00rootroot00000000000000cppcheck-htmlreport This is a little utility to generate a html report of a XML file produced by cppcheck. The utility is implemented in Python and require the pygments module to be able 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.66/htmlreport/check.sh000077500000000000000000000021021236713773000170460ustar00rootroot00000000000000#!/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 > errorlist.xml xmllint --noout errorlist.xml ./cppcheck-htmlreport --file ./errorlist.xml --title "errorlist" --report-dir . echo "" ../cppcheck --errorlist --inconclusive --xml-version=2 > errorlist.xml xmllint --noout errorlist.xml ./cppcheck-htmlreport --file ./errorlist.xml --title "errorlist" --report-dir . cppcheck-1.66/htmlreport/cppcheck-htmlreport000077500000000000000000000372771236713773000213630ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import unicode_literals import io import sys import optparse import os 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 """ 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; } .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; } .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" 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': t = t.replace('\n', HTML_INCONCLUSIVE % error['msg']) except KeyError: 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': try: self.errors.append({ 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'inconclusive': attributes['inconclusive'] }) except KeyError: self.errors.append({ 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'] }) 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') # 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: 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') as output_file: output_file.write(HTML_HEAD % (options.title, htmlFormatter.get_style_defs('.highlight'), options.title, filename)) 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 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(stats).most_common()[0][1] except IndexError: cnt_max = 0 try: cnt_min = Counter(stats).most_common()[-1][1] except IndexError: cnt_min = 0 for occurrences in reversed(range(cnt_min, cnt_max+1)): for _id in [k for k, v in sorted(Counter(stats).items()) if v == occurrences]: stat_html.append(" " + str(dict(Counter(stats).most_common())[_id]) + " " + str(_id) + "
\n") output_file.write(HTML_HEAD.replace('id="menu" dir="rtl"', 'id="menu_index"', 1).replace("Defect list", "Defect summary", 1) % (options.title, '', options.title, '')) output_file.write('

\n' + ' ' + str(stats_count) + ' total

\n' + ''.join(stat_html) + '

') 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: 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 if error['severity'] == 'error': error_class = 'class="error"' if error['id'] == 'missingInclude': output_file.write( '\n ' % (error['id'], error['severity'], error['msg'])) else: output_file.write( "\n " % (data['htmlfile'], error['line'], error['line'], error['id'], error['severity'], error_class, error['msg'])) output_file.write('\n
LineIdSeverityMessage
%s
Could not generated due to UnicodeDecodeError
%s
%s%s%s
%d%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("\nOpen '" + options.report_dir + "/index.html' to see the results.") cppcheck-1.66/htmlreport/example.cc000066400000000000000000000000711236713773000173770ustar00rootroot00000000000000#include "missing.h" int main() { int x; x++; } cppcheck-1.66/htmlreport/example.xml000066400000000000000000000007531236713773000176210ustar00rootroot00000000000000 Checking example.cc... Checking usage of global functions.. cppcheck-1.66/htmlreport/setup.py000077500000000000000000000002741236713773000171570ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup if __name__ == '__main__': setup( name="cppcheck", scripts=[ "cppcheck-htmlreport", ] ) cppcheck-1.66/htmlreport/test_htmlreport.py000077500000000000000000000070071236713773000212570ustar00rootroot00000000000000#!/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('. */ //--------------------------------------------------------------------------- // 64-bit portability //--------------------------------------------------------------------------- #include "check.h" #include //--------------------------------------------------------------------------- Check::Check(const std::string &aname) : _tokenizer(0), _settings(0), _errorLogger(0), _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(true, 1) << std::endl; } cppcheck-1.66/lib/check.h000066400000000000000000000113741236713773000152410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "token.h" #include "tokenize.h" #include "settings.h" #include "errorlogger.h" #include #include /// @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() { static std::list _instances; return _instances; } /** * analyse code - must be thread safe * @param tokens The tokens to analyse * @param result container where results are stored */ virtual void analyse(const Token *tokens, std::set &result) const { // suppress compiler warnings (void)tokens; (void)result; } /** * Save analysis data - the caller ensures thread safety * @param data The data where the results are saved */ virtual void saveAnalysisData(const std::set &data) const { // suppress compiler warnings (void)data; } /** 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); bool inconclusiveFlag() const { return _settings && _settings->inconclusive; } 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, bool inconclusive = false) { std::list callstack(1, tok); reportError(callstack, severity, id, msg, inconclusive); } /** report an error */ template void reportError(const std::list &callstack, Severity::SeverityType severity, const T id, const U msg, bool inconclusive = false) { ErrorLogger::ErrorMessage errmsg(callstack, _tokenizer?&_tokenizer->list:0, severity, id, msg, inconclusive); if (_errorLogger) _errorLogger->reportErr(errmsg); else reportError(errmsg); } private: const std::string _name; /** disabled assignment operator and copy constructor */ void operator=(const Check &); Check(const Check &); }; /// @} //--------------------------------------------------------------------------- #endif // checkH cppcheck-1.66/lib/check64bit.cpp000066400000000000000000000167571236713773000164570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "symboldatabase.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { Check64BitPortability instance; } /** Is given variable a pointer or array? */ static bool isaddr(const Variable *var) { return (var && (var->isPointer() || var->isArray())); } /** Is given variable an integer variable */ static bool isint(const Variable *var) { return (var && Token::Match(var->nameToken()->previous(), "int|long|DWORD %var% !![")); } void Check64BitPortability::pointerassignment() { if (!_settings->isEnabled("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 == 0 || !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()) { if (Token::Match(tok, "return %var%|%num% [;+]") && !Token::simpleMatch(tok, "return 0 ;")) { enum { NO, INT, PTR, PTRDIFF } type = NO; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if ((type == NO || type == INT) && Token::Match(tok2, "%var% [+;]") && isaddr(tok2->variable())) type = PTR; else if (type == NO && (tok2->isNumber() || isint(tok2->variable()))) type = INT; else if (type == PTR && Token::Match(tok2, "- %var%") && isaddr(tok2->next()->variable())) type = PTRDIFF; else if (tok2->str() == "(") { // TODO: handle parentheses type = NO; break; } else if (type == PTR && Token::simpleMatch(tok2, ".")) type = NO; // Reset after pointer reference, see #4642 else if (tok2->str() == ";") break; } if (retPointer && (type == INT || type == PTRDIFF)) returnIntegerError(tok); else if (!retPointer && type == PTR) 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 (Token::Match(tok, "[;{}] %var% = %var% [;+]")) { const Variable *var1(tok->next()->variable()); const Variable *var2(tok->tokAt(3)->variable()); if (isaddr(var1) && isint(var2) && tok->strAt(4) != "+") assignmentIntegerToAddressError(tok->next()); else if (isint(var1) && isaddr(var2) && !tok->tokAt(3)->isPointerCompare()) { // assigning address => warning // some trivial addition => warning if (Token::Match(tok->tokAt(4), "+ %any% !!;")) continue; assignmentAddressToIntegerError(tok->next()); } } } } } 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)."); } 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)."); } 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."); } 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."); } cppcheck-1.66/lib/check64bit.h000066400000000000000000000060671236713773000161150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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(0, settings, errorLogger); c.assignmentAddressToIntegerError(0); c.assignmentIntegerToAddressError(0); c.returnIntegerError(0); c.returnPointerError(0); } 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.66/lib/checkassert.cpp000066400000000000000000000145711236713773000170200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "symboldatabase.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckAssert instance; } void CheckAssert::assertWithSideEffects() { if (!_settings->isEnabled("warning")) return; const Token *tok = findAssertPattern(_tokenizer->tokens()); const Token *endTok = tok ? tok->next()->link() : nullptr; while (tok && endTok) { for (const Token* tmp = tok->next(); tmp != endTok; tmp = tmp->next()) { checkVariableAssignment(tmp, true); if (tmp->isName() && tmp->type() == Token::eFunction) { const Function* f = tmp->function(); if (f->argCount() == 0 && f->isConst) continue; // functions with non-const references else if (f->argCount() != 0) { for (std::list::const_iterator it = f->argumentList.begin(); it != f->argumentList.end(); ++it) { if (it->isConst() || it->isLocal()) continue; else if (it->isReference()) { const Token* next = it->nameToken()->next(); bool isAssigned = checkVariableAssignment(next, false); if (isAssigned) sideEffectInAssertError(tmp, f->name()); continue; } } } // variables in function scope const Scope* scope = f->functionScope; if (!scope) continue; for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) { if (tok2->type() != Token::eAssignmentOp && tok2->type() != Token::eIncDecOp) continue; const Variable* var = tok2->previous()->variable(); if (!var || var->isLocal()) continue; if (var->isArgument() && (var->isConst() || (!var->isReference() && !var->isPointer()))) // see ticket #4937. Assigning function arguments not passed by reference is ok. continue; std::vector returnTokens; // find all return statements for (const Token *rt = scope->classStart; rt != scope->classEnd; rt = rt->next()) { if (!Token::Match(rt, "return %any%")) continue; returnTokens.push_back(rt); } bool noReturnInScope = true; for (std::vector::iterator rt = returnTokens.begin(); rt != returnTokens.end(); ++rt) { if (inSameScope(*rt, tok2)) { noReturnInScope = false; break; } } if (noReturnInScope) continue; bool isAssigned = checkVariableAssignment(tok2, false); if (isAssigned) sideEffectInAssertError(tmp, f->name()); } } } tok = findAssertPattern(endTok->next()); endTok = tok ? tok->next()->link() : nullptr; } } //--------------------------------------------------------------------------- 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."); } 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."); } // checks if side effects happen on the variable prior to tmp bool CheckAssert::checkVariableAssignment(const Token* assignTok, bool reportErr /*= true*/) { const Variable* v = assignTok->previous()->variable(); if (!v) return false; // assignment if (assignTok->isAssignmentOp() || assignTok->type() == Token::eIncDecOp) { if (v->isConst()) return false; if (reportErr) // report as variable assignment error assignmentInAssertError(assignTok, v->name()); return true; } return false; // TODO: function calls on v } const Token* CheckAssert::findAssertPattern(const Token* start) { return Token::findmatch(start, "assert ( %any%"); } 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.66/lib/checkassert.h000066400000000000000000000051071236713773000164600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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: bool checkVariableAssignment(const Token* tmp, bool reportErr = true); static bool inSameScope(const Token* returnTok, const Token* assignTok); static const Token* findAssertPattern(const Token *start); 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(0, settings, errorLogger); c.sideEffectInAssertError(0, "function"); c.assignmentInAssertError(0, "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.66/lib/checkassignif.cpp000066400000000000000000000321661236713773000173220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 assignment / condition mismatches //--------------------------------------------------------------------------- #include "checkassignif.h" #include "symboldatabase.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckAssignIf instance; } void CheckAssignIf::assignIf() { if (!_settings->isEnabled("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 == 0) 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); } } } /** parse scopes recursively */ bool CheckAssignIf::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 (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 ((bitop == '&') && (0 == (num & num2))) mismatchingBitAndError(assignTok, num, tok2, num2); } if (Token::Match(tok2, "%varid% =", varid)) { if (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 ((bitop == '&') && (0 == (num & num2))) mismatchingBitAndError(assignTok, num, tok2, num2); } return true; } if (Token::Match(tok2, "++|-- %varid%", varid) || Token::Match(tok2, "%varid% ++|--", varid)) return true; if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid)) { unsigned int argumentNumber = 0; const Token *ftok; for (ftok = tok2; ftok && ftok->str() != "("; ftok = ftok->previous()) { if (ftok->str() == ")") ftok = ftok->link(); 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 == nullptr || par->isReference() || par->isPointer()) 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, "%var% (") && !Token::simpleMatch(tok2->next()->link(), ") {")) return true; if (Token::Match(tok2, "if|while (")) { if (!islocal && tok2->str() == "while") 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% %any% %num% &&|%oror%|)", varid)) { const Token *vartok = tok2->next(); const std::string& op(vartok->strAt(1)); const MathLib::bigint num2 = MathLib::toLongNumber(vartok->strAt(2)); const std::string condition(vartok->str() + op + vartok->strAt(2)); if (op == "==" && (num & num2) != ((bitop=='&') ? num2 : num)) assignIfError(assignTok, tok2, condition, false); else if (op == "!=" && (num & num2) != ((bitop=='&') ? num2 : num)) assignIfError(assignTok, tok2, condition, 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 CheckAssignIf::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") + "."); } void CheckAssignIf::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()); } 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); } void CheckAssignIf::comparison() { if (!_settings->isEnabled("style")) return; // Experimental code based on AST for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "==|!=")) { 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 ((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); } } } } } void CheckAssignIf::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); } extern bool isSameExpression(const Token *tok1, const Token *tok2, const std::set &constFunctions); static bool isOverlappingCond(const Token * const cond1, const Token * const cond2, const std::set &constFunctions) { if (!cond1 || !cond2) return false; // same expressions if (isSameExpression(cond1,cond2,constFunctions)) 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(expr1,expr2,constFunctions)) return false; const MathLib::bigint value1 = MathLib::toLongNumber(num1->str()); const MathLib::bigint value2 = MathLib::toLongNumber(num2->str()); return ((value1 & value2) == value2); } return false; } void CheckAssignIf::multiCondition() { if (!_settings->isEnabled("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 || !Token::simpleMatch(i->classDef, "if (")) continue; const Token * const cond1 = i->classDef->next()->astOperand2(); const Token * tok2 = i->classDef->next(); while (tok2) { 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(), _settings->library.functionpure)) multiConditionError(tok2, cond1->linenr()); } } } void CheckAssignIf::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()); } cppcheck-1.66/lib/checkassignif.h000066400000000000000000000101061236713773000167550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkassignifH #define checkassignifH //--------------------------------------------------------------------------- #include "config.h" #include "check.h" #include "mathlib.h" /// @addtogroup Checks /// @{ /** * @brief Check for assignment / condition mismatches */ class CPPCHECKLIB CheckAssignIf : public Check { public: /** This constructor is used when registering the CheckAssignIf */ CheckAssignIf() : Check(myName()) { } /** This constructor is used when running checks. */ CheckAssignIf(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckAssignIf checkAssignIf(tokenizer, settings, errorLogger); checkAssignIf.multiCondition(); } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckAssignIf checkAssignIf(tokenizer, settings, errorLogger); checkAssignIf.assignIf(); checkAssignIf.comparison(); } /** 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); /** mismatching lhs and rhs in comparison */ void comparison(); /** match 'if' and 'else if' conditions */ void multiCondition(); private: 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 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckAssignIf c(0, settings, errorLogger); c.assignIfError(0, 0, "", false); c.comparisonError(0, "&", 6, "==", 1, false); c.multiConditionError(0,1); c.mismatchingBitAndError(0,0xf0, 0, 1); } static std::string myName() { return "AssignIf"; } std::string classInfo() const { return "Match assignments and 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 matching 'if' and 'else if' conditions\n" "* Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkassignifH cppcheck-1.66/lib/checkautovariables.cpp000066400000000000000000000434521236713773000203600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "symboldatabase.h" #include //--------------------------------------------------------------------------- // Register this check class into cppcheck by creating a static instance of it.. namespace { static CheckAutoVariables instance; } bool CheckAutoVariables::isPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isPointer()); } 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; } return true; } bool CheckAutoVariables::isAutoVarArray(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isLocal() && !var->isStatic() && var->isArray() && !var->isPointer()); } // 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; const Token * const next = vartok->next(); if (Token::Match(vartok->previous(), "& %var% [") && var->isPointer()) return false; // &a.b[0] if (Token::Match(vartok, "%var% . %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() { if (!_settings->isEnabled("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, "[;{}] %var% =") && isNonReferenceArg(tok->next()) && !variableIsUsedInScope(Token::findsimplematch(tok->tokAt(2), ";"), tok->next()->varId(), scope)) { errorUselessAssignmentPtrArg(tok->next()); } } } } void CheckAutoVariables::autoVariables() { 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 (_settings->inconclusive) { const Variable * var1 = tok->next()->variable(); if (var1 && var1->isArgument() && var1->isPointer()) { 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 (_settings->inconclusive) { const Variable * var1 = tok->next()->variable(); if (var1 && var1->isArgument() && var1->isPointer()) { 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()) && 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% ;") && isAutoVar(tok->tokAt(2))) { errorReturnAddressToAutoVariable(tok); } else if (Token::Match(tok, "return & %var% [") && Token::simpleMatch(tok->linkAt(3), "] ;") && isAutoVarArray(tok->tokAt(2))) { errorReturnAddressToAutoVariable(tok); } else if (Token::Match(tok, "return & %var% ;") && tok->tokAt(2)->varId()) { const Variable * var1 = tok->tokAt(2)->variable(); if (var1 && var1->isArgument() && var1->typeEndToken()->str() != "&") errorReturnAddressOfFunctionParameter(tok, tok->strAt(2)); } // Invalid pointer deallocation else if (Token::Match(tok, "free ( %var% ) ;") || (_tokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isAutoVarArray(tok)) errorInvalidDeallocation(tok); } else if (Token::Match(tok, "free ( & %var% ) ;") || (_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 (Token::Match(tok2, "return %var% ;")) { if (isAutoVarArray(tok2->next())) { errorReturnPointerToLocalArray(tok2); } } } } } } void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of an auto-variable returned."); } void CheckAutoVariables::errorReturnPointerToLocalArray(const Token *tok) { reportError(tok, Severity::error, "returnLocalVariable", "Pointer to local array variable returned."); } 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."); } 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.", 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."); } void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok) { reportError(tok, Severity::warning, "uselessAssignmentPtrArg", "Assignment of function parameter has no effect outside the function."); } //--------------------------------------------------------------------------- // return temporary? bool CheckAutoVariables::returnTemporary(const Token *tok) const { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 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 (symbolDatabase->isClassOrStruct(start->str())) retvalue = true; else retref = true; } } func = true; } if (!func && symbolDatabase->isClassOrStruct(tok->str())) return true; return bool(!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()) 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() && !Token::Match(tok->astParent(), "<<|>>")) 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() { 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->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 %var% (") && 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."); } void CheckAutoVariables::errorReturnTempReference(const Token *tok) { reportError(tok, Severity::error, "returnTempReference", "Reference to temporary returned."); } 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."); } cppcheck-1.66/lib/checkautovariables.h000066400000000000000000000110451236713773000200160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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 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 */ bool returnTemporary(const Token *tok) const; 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 errorUselessAssignmentPtrArg(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckAutoVariables c(0,settings,errorLogger); c.errorAutoVariableAssignment(0, false); c.errorReturnAddressToAutoVariable(0); c.errorReturnPointerToLocalArray(0); c.errorReturnReference(0); c.errorReturnTempReference(0); c.errorInvalidDeallocation(0); c.errorReturnAddressOfFunctionParameter(0, "parameter"); c.errorUselessAssignmentPtrArg(0); } 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" "* useless assignment of pointer parameter\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkautovariablesH cppcheck-1.66/lib/checkbool.cpp000066400000000000000000000542701236713773000164520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mathlib.h" #include "symboldatabase.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckBool instance; } static bool astIsBool(const Token *expr) { return Token::Match(expr, "%comp%|%bool%|%oror%|&&|!") && !expr->link(); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckBool::checkIncrementBoolean() { if (!_settings->isEnabled("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% ++")) { if (tok->varId()) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "bool") 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." ); } //--------------------------------------------------------------------------- // if (bool & bool) -> if (bool && bool) // if (bool | bool) -> if (bool || bool) //--------------------------------------------------------------------------- void CheckBool::checkBitwiseOnBoolean() { if (!_settings->isEnabled("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 (var && var->typeEndToken()->str() == "bool") { 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 (var && var->typeEndToken()->str() == "bool") { 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 + "'?", true); } //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- static bool isBool(const Variable* var) { return (var && var->typeEndToken()->str() == "bool"); } static bool isNonBoolStdType(const Variable* var) { return (var && var->typeEndToken()->isStandardType() && var->typeEndToken()->str() != "bool"); } void CheckBool::checkComparisonOfBoolWithInt() { if (!_settings->isEnabled("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()) { if ((!Token::Match(tok->previous(), "%cop%")) && Token::Match(tok->next(), "%comp%") && (!Token::Match(tok->tokAt(3), "%cop%"))) { const Token* const right = tok->tokAt(2); if ((tok->varId() && right->isNumber()) || (tok->isNumber() && right->varId())) { // Comparing variable with number const Token* varTok = tok; const Token* numTok = right; if (tok->isNumber() && right->varId()) // num with var std::swap(varTok, numTok); if (isBool(varTok->variable()) && // Variable has to be a boolean ((tok->strAt(1) != "==" && tok->strAt(1) != "!=") || (MathLib::toLongNumber(numTok->str()) != 0 && MathLib::toLongNumber(numTok->str()) != 1))) { // == 0 and != 0 are allowed, for C also == 1 and != 1 comparisonOfBoolWithIntError(varTok, numTok->str(), tok->strAt(1) == "==" || tok->strAt(1) == "!="); } } else if (tok->isBoolean() && right->varId()) { // Comparing boolean constant with variable if (isNonBoolStdType(right->variable())) { // Variable has to be of non-boolean standard type comparisonOfBoolWithIntError(right, tok->str(), false); } else if (tok->strAt(1) != "==" && tok->strAt(1) != "!=") { comparisonOfBoolWithInvalidComparator(right, tok->str()); } } else if (tok->varId() && right->isBoolean()) { // Comparing variable with boolean constant if (isNonBoolStdType(tok->variable())) { // Variable has to be of non-boolean standard type comparisonOfBoolWithIntError(tok, right->str(), false); } else if (tok->strAt(1) != "==" && tok->strAt(1) != "!=") { comparisonOfBoolWithInvalidComparator(right, tok->str()); } } else if (tok->isNumber() && right->isBoolean()) { // number constant with boolean constant comparisonOfBoolWithIntError(tok, right->str(), false); } else if (tok->isBoolean() && right->isNumber()) { // number constant with boolean constant comparisonOfBoolWithIntError(tok, tok->str(), false); } else if (tok->varId() && right->varId()) { // Comparing two variables, one of them boolean, one of them integer const Variable* var1 = right->variable(); const Variable* var2 = tok->variable(); if (isBool(var1) && isNonBoolStdType(var2)) // Comparing boolean with non-bool standard type comparisonOfBoolWithIntError(tok, var1->name(), false); else if (isNonBoolStdType(var1) && isBool(var2)) // Comparing non-bool standard type with boolean comparisonOfBoolWithIntError(tok, var2->name(), false); } } } } } void CheckBool::comparisonOfBoolWithIntError(const Token *tok, const std::string &expression, bool n0o1) { if (n0o1) reportError(tok, Severity::warning, "comparisonOfBoolWithInt", "Comparison of a boolean with an integer that is neither 1 nor 0.\n" "The expression '" + expression + "' is of type 'bool' " "and it is compared against an integer value that is " "neither 1 nor 0."); else reportError(tok, Severity::warning, "comparisonOfBoolWithInt", "Comparison of a boolean with an integer.\n" "The expression '" + expression + "' is of type 'bool' " "and it is compared against an integer value."); } 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 //------------------------------------------------------------------------------- void CheckBool::checkComparisonOfFuncReturningBool() { if (!_settings->isEnabled("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->type() != Token::eComparisonOp || tok->str() == "==" || tok->str() == "!=") continue; const Token *first_token = tok->previous(); if (tok->strAt(-1) == ")") { first_token = first_token->link()->previous(); } bool first_token_func_of_type_bool = false; if (Token::Match(first_token, "%var% (") && !Token::Match(first_token->previous(), "::|.")) { const Function* func = first_token->function(); if (func && func->tokenDef && func->tokenDef->strAt(-1) == "bool") { first_token_func_of_type_bool = true; } } Token *second_token = tok->next(); while (second_token->str()=="!") { second_token = second_token->next(); } bool second_token_func_of_type_bool = false; if (Token::Match(second_token, "%var% (") && !Token::Match(second_token->previous(), "::|.")) { const Function* func = second_token->function(); if (func && func->tokenDef && func->tokenDef->strAt(-1) == "bool") { second_token_func_of_type_bool = true; } } if ((first_token_func_of_type_bool == true) && (second_token_func_of_type_bool == true)) { comparisonOfTwoFuncsReturningBoolError(first_token->next(), first_token->str(), second_token->str()); } else if (first_token_func_of_type_bool == true) { comparisonOfFuncReturningBoolError(first_token->next(), first_token->str()); } else if (second_token_func_of_type_bool == true) { comparisonOfFuncReturningBoolError(second_token->previous(), second_token->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."); } 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."); } //------------------------------------------------------------------------------- // 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("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->type() != Token::eComparisonOp || tok->str() == "==" || tok->str() == "!=") continue; bool first_token_bool = false; const Token *first_token = tok->previous(); if (first_token->varId()) { if (isBool(first_token->variable())) { first_token_bool = true; } } if (!first_token_bool) continue; bool second_token_bool = false; const Token *second_token = tok->next(); if (second_token->varId()) { if (isBool(second_token->variable())) { second_token_bool = true; } } if (second_token_bool) { comparisonOfBoolWithBoolError(first_token->next(), first_token->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."); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- 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 = 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."); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolExpressionWithInt() { if (!_settings->isEnabled("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; // Skip template parameters if (tok->link() && tok->str() == "<") { tok = tok->link(); continue; } const Token* numTok = 0; const Token* boolExpr = 0; 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 (Token::Match(boolExpr,"%bool%")) // The CheckBool::checkComparisonOfBoolWithInt warns about this. continue; if (boolExpr->isOp() && numTok->isName() && Token::Match(tok, "==|!=")) // there is weird code such as: ((aisNumber()) { if (numTok->str() == "0" && Token::Match(tok, numInRhs ? ">|==|!=" : "<|==|!=")) continue; if (numTok->str() == "1" && Token::Match(tok, numInRhs ? "<|==|!=" : ">|==|!=")) continue; comparisonOfBoolExpressionWithIntError(tok, true); } else if (isNonBoolStdType(numTok->variable())) 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."); else reportError(tok, Severity::warning, "compareBoolExpressionWithInt", "Comparison of a boolean expression with an integer."); } void CheckBool::pointerArithBool() { 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, "if|while (")) { pointerArithBoolCond(tok->next()->astOperand2()); } } } } void CheckBool::pointerArithBoolCond(const Token *tok) { if (!tok) return; if (Token::Match(tok, "&&|%oror%")) { pointerArithBoolCond(tok->astOperand1()); pointerArithBoolCond(tok->astOperand2()); return; } if (tok->str() != "+") return; if (tok->astOperand1() && 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."); } void CheckBool::checkAssignBoolToFloat() { if (!_tokenizer->isCPP()) return; if (!_settings->isEnabled("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 (Token::Match(tok, "%var% =")) { const Variable * const var = symbolDatabase->getVariableFromVarId(tok->varId()); if (var && var->isFloatingType() && astIsBool(tok->next()->astOperand2())) assignBoolToFloatError(tok->next()); } } } } void CheckBool::assignBoolToFloatError(const Token *tok) { reportError(tok, Severity::style, "assignBoolToFloat", "Boolean value assigned to floating point variable."); } cppcheck-1.66/lib/checkbool.h000066400000000000000000000135531236713773000161160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" class Function; class Variable; /// @addtogroup Checks /// @{ /** @brief checks dealing with suspicous 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 comparisonOfBoolWithIntError(const Token *tok, const std::string &expression, bool n0o1); 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(0, settings, errorLogger); c.assignBoolToPointerError(0); c.comparisonOfFuncReturningBoolError(0, "func_name"); c.comparisonOfTwoFuncsReturningBoolError(0, "func_name1", "func_name2"); c.comparisonOfBoolWithBoolError(0, "var_name"); c.incrementBooleanError(0); c.comparisonOfBoolWithIntError(0, "varname", true); c.bitwiseOnBooleanError(0, "varname", "&&"); c.comparisonOfBoolExpressionWithIntError(0, true); c.pointerArithBoolError(0); } static std::string myName() { return "Boolean"; } std::string classInfo() const { return "Boolean type checks\n" "* using increment on boolean\n" "* comparison of a boolean with a non-zero integer\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"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboolH cppcheck-1.66/lib/checkboost.cpp000066400000000000000000000046561236713773000166500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "symboldatabase.h" // Register this check class (by creating a static instance of it) namespace { CheckBoost instance; } 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 *container_tok = tok->next()->link()->previous(); if (!Token::Match(container_tok, "%var% ) {")) continue; const unsigned int container_id = container_tok->varId(); if (container_id == 0) continue; const Token *tok2 = container_tok->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", container_id)) { 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." ); } cppcheck-1.66/lib/checkboost.h000066400000000000000000000047411236713773000163100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" 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(0, settings, errorLogger); c.boostForeachError(0); } 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.66/lib/checkbufferoverrun.cpp000066400000000000000000002274361236713773000204170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "tokenize.h" #include "mathlib.h" #include "symboldatabase.h" #include #include #include #include // <- assert #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckBufferOverrun instance; } //--------------------------------------------------------------------------- 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()); } void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector &index) { 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."; } const Token *condition = nullptr; for (std::size_t i = 0; i < index.size(); ++i) { if (condition == nullptr) condition = index[i].condition; } if (condition != nullptr) { errmsg << " Otherwise condition '" << condition->expressionString() << "' is redundant."; std::list callstack; callstack.push_back(tok); callstack.push_back(condition); reportError(callstack, Severity::warning, "arrayIndexOutOfBoundsCond", errmsg.str()); } else { reportError(tok, Severity::error, "arrayIndexOutOfBounds", errmsg.str()); } } 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)); } 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."); 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."); } void CheckBufferOverrun::possibleReadlinkBufferOverrunError(const Token* tok, const std::string &funcname, const std::string &varname) { const std::string errmsg = funcname + "() might return the full size of '" + varname + "'. Lower the supplied size by one.\n" + funcname + "() might return the full size of '" + varname + "'. " "If a " + varname + "[len] = '\\0'; statement follows, it will overrun the buffer. Lower the supplied size by one."; reportError(tok, Severity::warning, "possibleReadlinkBufferOverrun", errmsg, true); } void CheckBufferOverrun::strncatUsageError(const Token *tok) { if (_settings && !_settings->isEnabled("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"); } 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()); } void CheckBufferOverrun::pointerOutOfBoundsError(const Token *tok, const std::string &object) { reportError(tok, Severity::portability, "pointerOutOfBounds", "Undefined behaviour: Pointer arithmetic result does not point into or just past the end of the " + object + ".\n" "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("warning")) return; reportError(tok, Severity::warning, "sizeArgumentAsChar", "The size argument is given as a char constant."); } 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.", true); } void CheckBufferOverrun::cmdLineArgsError(const Token *tok) { reportError(tok, Severity::error, "insecureCmdLineArgs", "Buffer overrun possible for long command line arguments."); } 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, 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."); } 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."); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check array usage.. //--------------------------------------------------------------------------- /** * 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::list &minsizes, const Token * const ftok, const std::size_t arraySize, const Token **charSizeToken) { if (charSizeToken) *charSizeToken = nullptr; if (minsizes.empty()) return false; // All conditions must be true bool error = true; for (std::list::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 ((std::size_t)sz > arraySize) error = true; } else if (argtok->type() == 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 ((std::size_t)sz > arraySize) error = true; } break; case Library::ArgumentChecks::MinSize::STRLEN: if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize) error = true; break; case Library::ArgumentChecks::MinSize::SIZEOF: if (argtok->type() == 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 par, const ArrayInfo &arrayInfo, const std::list& callstack) { const std::list * const minsizes = _settings->library.argminsizes(ftok.str(),par); if (minsizes && (!(Token::simpleMatch(ftok.previous(), ".") || Token::Match(ftok.tokAt(-2), "!!std ::")))) { if (arrayInfo.element_size() == 0) return; MathLib::bigint arraySize = arrayInfo.element_size(); for (std::size_t i = 0; i < arrayInfo.num().size(); ++i) arraySize *= arrayInfo.num(i); const Token *charSizeToken = nullptr; if (checkMinSizes(*minsizes, &ftok, (std::size_t)arraySize, &charSizeToken)) 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(par-1); // Ensure that it has a compatible size.. if (!parameter || _tokenizer->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(), "-- %var%") || Token::Match(ftok2, "%var% --")) break; if (Token::Match(ftok2->previous(), ";|{|}|%op% %var% [ %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; indexes.push_back(index); arrayIndexOutOfBoundsError(callstack2, arrayInfo, indexes); } } } // Calling function.. if (Token::Match(ftok2, "%var% (")) { ArrayInfo ai(arrayInfo); ai.declarationId(parameter->declarationId()); checkFunctionCall(ftok2, ai, callstack); } } } } // Check 'float x[10]' arguments in declaration if (_settings->isEnabled("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(par-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 = _tokenizer->sizeOfType(argument->typeStartToken()); if (argsize == 100) // unknown size argsize = 0; while (Token::Match(tok2, "[ %num% ] [,)[]")) { argsize *= MathLib::toLongNumber(tok2->strAt(1)); tok2 = tok2->tokAt(3); } 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.begin(); it != callstack.end(); ++it) { // Same function name => bail out if (tok->str() == (*it)->str()) return; } callstack.push_back(tok); const unsigned int declarationId = arrayInfo.declarationId(); const Token *tok2 = tok->tokAt(2); // 1st parameter.. if (Token::Match(tok2, "%varid% ,|)", declarationId)) checkFunctionParameter(*tok, 1, arrayInfo, callstack); else if (Token::Match(tok2, "%varid% + %num% ,|)", declarationId)) { const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(2)))); checkFunctionParameter(*tok, 1, ai, callstack); } // goto 2nd parameter and check it.. tok2 = tok2->nextArgument(); if (Token::Match(tok2, "%varid% ,|)", declarationId)) checkFunctionParameter(*tok, 2, arrayInfo, callstack); else if (Token::Match(tok2, "%varid% + %num% ,|)", declarationId)) { const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(2)))); checkFunctionParameter(*tok, 2, ai, callstack); } } 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 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 unsigned char varcount = static_cast(varname.empty() ? 0U : (varname.size() - 1) * 2U); // ValueFlow array index.. if ((declarationId > 0 && Token::Match(tok, "%varid% [", declarationId)) || (declarationId == 0 && Token::Match(tok, (varnames + " [").c_str()))) { const Token *tok2 = tok; 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 isPortabilityEnabled = _settings->isEnabled("portability"); for (const Token* const end = tok->scope()->classEnd; 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% [ %num% ]", declarationId))) || (declarationId == 0 && ((tok->str() == "return" || (!tok->isName() && !Token::Match(tok, "[.&]"))) && (Token::Match(tok->next(), (varnames + " [ %num% ]").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.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); } // totalElements == 0 => Unknown size if (totalElements == 0) continue; const Token *tok3 = tok->previous(); while (tok3 && Token::Match(tok3->previous(), "%var% .")) 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 (_settings->inconclusive) { 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 && size > 0) { std::list callstack; callstack.push_back(tok); if (Token::Match(tok, ("%var% ( " + varnames + " ,").c_str())) checkFunctionParameter(*tok, 1, arrayInfo, callstack); if (Token::Match(tok, ("%var% ( %var% , " + varnames + " ,").c_str())) checkFunctionParameter(*tok, 2, arrayInfo, callstack); } // Writing data into array.. if ((declarationId > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", declarationId)) || (declarationId == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %str% )").c_str()))) { const std::size_t len = Token::getStrLength(tok->tokAt(varcount + 4)); if (total_size > 0 && len >= (unsigned int)total_size) { bufferOverrunError(tok, declarationId > 0 ? emptyString : varnames); continue; } } else if ((declarationId > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %var% )", declarationId)) || (declarationId == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %var% )").c_str()))) { const Variable *var = tok->tokAt(4)->variable(); if (var && var->isArray() && var->dimensions().size() == 1) { const std::size_t len = (std::size_t)var->dimension(0); if (total_size > 0 && len > (unsigned int)total_size) { if (_settings->inconclusive) 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; while (Token::Match(tok2, strcatPattern.c_str(), declarationId)) { charactersAppend += Token::getStrLength(tok2->tokAt(4 + varcount)); if (charactersAppend >= static_cast(total_size)) { bufferOverrunError(tok2); break; } tok2 = tok2->tokAt(7 + varcount); } } // sprintf.. // TODO: change total_size to an unsigned value and remove the "&& total_size > 0" check. const std::string sprintfPattern = declarationId > 0 ? std::string("sprintf ( %varid% , %str% [,)]") : ("sprintf ( " + varnames + " , %str% [,)]"); if (Token::Match(tok, sprintfPattern.c_str(), declarationId) && total_size > 0) { checkSprintfCall(tok, static_cast(total_size)); } // snprintf.. const std::string snprintfPattern = declarationId > 0 ? std::string("snprintf ( %varid% , %num% ,") : ("snprintf ( " + varnames + " , %num% ,"); if (Token::Match(tok, snprintfPattern.c_str(), declarationId)) { const MathLib::bigint n = MathLib::toLongNumber(tok->strAt(4 + varcount)); if (n > total_size) outOfBoundsError(tok->tokAt(4 + varcount), "snprintf size", true, n, total_size); } // Check function call.. if (Token::Match(tok, "%var% (") && total_size > 0) { // 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 else if (declarationId && Token::Match(tok, "= %varid% + %num% ;", declarationId)) { const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3)); if (isPortabilityEnabled && index > size) pointerOutOfBoundsError(tok->next(), "buffer"); 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); } } } void CheckBufferOverrun::valueFlowCheckArrayIndex(const Token * const tok, const ArrayInfo &arrayInfo) { // Taking address? bool addressOf = false; { const Token *tok2 = tok->astParent(); while (Token::Match(tok2, "%var%|.|::|[")) tok2 = tok2->astParent(); addressOf = tok2 && tok2->str() == "&" && !(tok2->astOperand1() && tok2->astOperand2()); } // 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.. std::vector indexes; unsigned int valuevarid = 0; for (const Token *tok2 = tok; indexes.size() < arrayInfo.num().size() && Token::Match(tok2, "["); tok2 = tok2->link()->next()) { if (!tok2->astOperand2()) { indexes.clear(); break; } const ValueFlow::Value *value = tok2->astOperand2()->getMaxValue(warn == 1); if (!value) { indexes.clear(); break; } if (valuevarid == 0U) valuevarid = value->varId; if (value->varId > 0 && valuevarid != value->varId) { indexes.clear(); break; } if (value->intvalue < 0) { indexes.clear(); break; } indexes.push_back(*value); } 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].intvalue * totalElements; totalElements *= arrayInfo.num(ri); } // totalElements <= 0 => Unknown size if (totalElements <= 0) continue; // taking address of 1 past end? if (addressOf && totalIndex == totalElements) continue; // Is totalIndex in bounds? if (totalIndex >= totalElements) { arrayIndexOutOfBoundsError(tok, arrayInfo, indexes); break; } // 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].intvalue >= arrayInfo.num(i)) { // The access is still within the memory range for the array // so it may be intentional. if (_settings->inconclusive) { arrayIndexOutOfBoundsError(tok, arrayInfo, indexes); break; // only warn about the first one } } } } } } } void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo) { const MathLib::bigint total_size = arrayInfo.num(0) * arrayInfo.element_size(); const Token *scope_begin = tok->previous(); assert(scope_begin != 0); const unsigned int declarationId = arrayInfo.declarationId(); const bool isPortabilityEnabled = _settings->isEnabled("portability"); const bool isWarningEnabled = _settings->isEnabled("warning"); for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) { if (tok->varId() == declarationId) { if (tok->strAt(1) == "[") { valueFlowCheckArrayIndex(tok->next(), arrayInfo); } // undefined behaviour: result of pointer arithmetic is out of bounds else if (isPortabilityEnabled && Token::Match(tok->previous(), "= %varid% + %num% ;", declarationId)) { const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(2)); if (index < 0 || index > arrayInfo.num(0)) { pointerOutOfBoundsError(tok, "array"); } } } else if (!tok->scope()->isExecutable()) // No executable code outside of executable scope - continue to increase performance continue; else if (Token::Match(tok, "%var% (")) { // Check function call.. checkFunctionCall(tok, arrayInfo, std::list()); if (_settings->inconclusive && Token::Match(tok, "strncpy|memcpy|memmove ( %varid% , %str% , %num% )", declarationId)) { if (Token::getStrLength(tok->tokAt(4)) >= (unsigned int)total_size) { const unsigned int num = (unsigned int)MathLib::toLongNumber(tok->strAt(6)); if ((unsigned int)total_size == num) bufferNotZeroTerminatedError(tok, tok->strAt(2), tok->str()); } } if ((Token::Match(tok, "strncpy|strncat ( %varid% ,", declarationId) && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )"))) { const Token* param3 = tok->linkAt(1)->previous(); // check for strncpy which is not terminated if (tok->str() == "strncpy") { // strncpy takes entire variable length as input size unsigned int num = (unsigned int)MathLib::toLongNumber(param3->str()); // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 if (isWarningEnabled && num >= total_size && _settings->inconclusive) { const Token *tok2 = tok->next()->link()->next(); for (; tok2; tok2 = tok2->next()) { if (tok2->varId() == tok->tokAt(2)->varId()) { if (!Token::Match(tok2, "%varid% [ %any% ] = 0 ;", tok->tokAt(2)->varId())) { terminateStrncpyError(tok, tok->strAt(2)); } break; } } } } // Dangerous usage of strncat.. else if (tok->str() == "strncat") { const MathLib::bigint n = MathLib::toLongNumber(param3->str()); if (n >= total_size) strncatUsageError(tok); } // Dangerous usage of strncpy + strncat.. if (Token::Match(param3->tokAt(2), "; strncat ( %varid% ,", declarationId) && Token::Match(param3->linkAt(4)->tokAt(-2), ", %num% )")) { const MathLib::bigint n = MathLib::toLongNumber(param3->str()) + MathLib::toLongNumber(param3->linkAt(4)->strAt(-1)); if (n > total_size) strncatUsageError(param3->tokAt(3)); } } // Writing data into array.. if (Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", declarationId)) { const std::size_t len = Token::getStrLength(tok->tokAt(4)); if (total_size > 0 && len >= (unsigned int)total_size) { bufferOverrunError(tok, arrayInfo.varname()); continue; } } // Detect few strcat() calls if (total_size > 0 && Token::Match(tok, "strcat ( %varid% , %str% ) ;", declarationId)) { std::size_t charactersAppend = 0; const Token *tok2 = tok; while (tok2 && Token::Match(tok2, "strcat ( %varid% , %str% ) ;", declarationId)) { charactersAppend += Token::getStrLength(tok2->tokAt(4)); if (charactersAppend >= (unsigned int)total_size) { bufferOverrunError(tok2, arrayInfo.varname()); break; } tok2 = tok2->tokAt(7); } } if (Token::Match(tok, "sprintf ( %varid% , %str% [,)]", declarationId)) { checkSprintfCall(tok, total_size); } // snprintf.. if (total_size > 0 && Token::Match(tok, "snprintf ( %varid% , %num% ,", declarationId)) { const MathLib::bigint n = MathLib::toLongNumber(tok->strAt(4)); if (n > total_size) outOfBoundsError(tok->tokAt(4), "snprintf size", true, n, total_size); } // readlink() / readlinkat() buffer usage if (_settings->standards.posix && Token::Match(tok, "readlink|readlinkat (")) checkReadlinkBufferUsage(tok, scope_begin, declarationId, total_size); } } } //--------------------------------------------------------------------------- // Checking member variables of structs.. //--------------------------------------------------------------------------- bool CheckBufferOverrun::isArrayOfStruct(const Token* tok, int &position) { if (Token::Match(tok->next(), "%var% [ %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; } void CheckBufferOverrun::checkReadlinkBufferUsage(const Token* ftok, const Token *scope_begin, const unsigned int varid, const MathLib::bigint total_size) { const std::string& funcname = ftok->str(); const Token* bufParam = ftok->tokAt(2)->nextArgument(); if (funcname == "readlinkat") bufParam = bufParam ? bufParam->nextArgument() : nullptr; if (!Token::Match(bufParam, "%varid% , %num% )", varid)) return; const MathLib::bigint n = MathLib::toLongNumber(bufParam->strAt(2)); if (total_size > 0 && n > total_size) outOfBoundsError(bufParam, funcname + "() buf size", true, n, total_size); if (!_settings->inconclusive) return; // only writing a part of the buffer if (n < total_size) return; // readlink()/readlinkat() never terminates the buffer, check the end of the scope for buffer termination. bool found_termination = false; const Token *scope_end = scope_begin->link(); for (const Token *tok2 = bufParam->tokAt(4); tok2 && tok2 != scope_end; tok2 = tok2->next()) { if (Token::Match(tok2, "%varid% [ %any% ] = 0 ;", bufParam->varId())) { found_termination = true; break; } } if (!found_termination) { bufferNotZeroTerminatedError(ftok, bufParam->str(), funcname); } else if (n == total_size) { possibleReadlinkBufferOverrunError(ftok, funcname, bufParam->str()); } } //--------------------------------------------------------------------------- // 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% [ %num% ]")) { const std::size_t strLen = tok->str().size() - 2; // Don't count enclosing quotes const std::size_t index = (std::size_t)std::atoi(tok->strAt(2).c_str()); if (index > strLen) bufferOverrunError(tok, tok->str()); } } // check all known fixed size arrays first by just looking them up const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int i = 1; i <= _tokenizer->varIdCount(); i++) { const Variable *var = symbolDatabase->getVariableFromVarId(i); if (var && var->isArray() && var->dimension(0) > 0) { const ArrayInfo arrayInfo(var, _tokenizer, i); const Token *tok = var->nameToken(); while (tok && tok->str() != ";") { if (tok->str() == "{") { if (Token::simpleMatch(tok->previous(), "= {")) tok = tok->link(); else break; } tok = tok->next(); } if (!tok) break; if (tok->str() == "{") tok = tok->next(); checkScope(tok, arrayInfo); } } // 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 the previous token exists, it must be either a variable name or "[;{}]" if (tok->previous() && (!tok->previous()->isName() && !Token::Match(tok->previous(), "[;{}]"))) continue; // size : Max array index MathLib::bigint size = 0; // type : The type of a array element std::string type; // varid : The variable id for the array const Variable *var = nullptr; // nextTok : number of tokens used in variable declaration - used to skip to next statement. int nextTok = 0; _errorLogger->reportProgress(_tokenizer->list.getSourceFilePath(), "Check (BufferOverrun::checkGlobalAndLocalVariable)", tok->progressValue()); if (Token::Match(tok, "[*;{}] %var% = new %type% [ %num% ]")) { size = MathLib::toLongNumber(tok->strAt(6)); type = tok->strAt(4); var = tok->next()->variable(); nextTok = 8; if (size < 0) { negativeMemoryAllocationSizeError(tok->next()->next()); } } else if (Token::Match(tok, "[*;{}] %var% = new %type% ( %num% )")) { size = 1; type = tok->strAt(4); var = tok->next()->variable(); nextTok = 8; } else if (Token::Match(tok, "[;{}] %var% = %str% ;") && tok->next()->variable() && tok->next()->variable()->isPointer()) { size = 1 + int(tok->tokAt(3)->strValue().size()); type = "char"; var = tok->next()->variable(); nextTok = 4; } else if (Token::Match(tok, "[*;{}] %var% = malloc|alloca ( %num% ) ;")) { size = MathLib::toLongNumber(tok->strAt(5)); type = "char"; // minimum type, typesize=1 var = tok->next()->variable(); nextTok = 7; if (size < 0) { negativeMemoryAllocationSizeError(tok->next()->next()); } /** @todo false negatives: this may be too conservative */ if (!var || var->typeEndToken()->str() != "*" || var->typeStartToken()->next() != var->typeEndToken()) continue; // get name of variable type = var->typeStartToken()->str(); // malloc() gets count of bytes and not count of // elements, so we should calculate count of elements // manually const unsigned int sizeOfType = _tokenizer->sizeOfType(var->typeStartToken()); if (sizeOfType > 0) { size /= static_cast(sizeOfType); } if (size < 0) { negativeMemoryAllocationSizeError(tok->next()->next()); } } else { continue; } if (var == 0) continue; Token sizeTok(0); sizeTok.str(type); const MathLib::bigint total_size = size * static_cast(_tokenizer->sizeOfType(&sizeTok)); if (total_size == 0) continue; std::vector v; ArrayInfo temp(var->declarationId(), tok->next()->str(), total_size / size, size); checkScope(tok->tokAt(nextTok), v, temp); } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Checking member variables of structs.. //--------------------------------------------------------------------------- void CheckBufferOverrun::checkStructVariable() { const SymbolDatabase * symbolDatabase = _tokenizer->getSymbolDatabase(); // 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, _tokenizer); // 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; varname.push_back(""); varname.push_back(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); int pos = 2; for (int k = 0 ; k < posOfSemicolon; k++) { for (int index = pos; index < (pos + 3); index++) tok3->strAt(index); pos += 3; } } // 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), "; %var% = malloc ( %num% ) ;") || (Token::Match(tok3->tokAt(3), "; %var% = (") && 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() { checkGlobalAndLocalVariable(); checkStructVariable(); checkBufferAllocatedWithStrlen(); checkStringArgument(); checkInsecureCmdLineArgs(); } //--------------------------------------------------------------------------- void CheckBufferOverrun::bufferOverrun2() { // singlepass checking using ast, symboldatabase and valueflow for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%var% [")) { const Variable *var = tok->variable(); if (!var || var->nameToken() == tok || !var->isArray()) continue; // TODO: last array in struct.. if (var->dimension(0) <= 1 && Token::simpleMatch(var->nameToken()->linkAt(1),"] ; }")) continue; // TODO: what to do about negative index.. const Token *index = tok->next()->astOperand2(); if (index && index->getValueLE(-1LL,_settings)) continue; ArrayInfo arrayInfo(var,_tokenizer); // Set full varname.. if (tok->astParent() && tok->astParent()->str() == ".") { const Token *parent = tok->astParent(); while (parent->astParent() && parent->astParent()->str() == ".") parent = parent->astParent(); arrayInfo.varname(parent->expressionString()); } valueFlowCheckArrayIndex(tok->next(), arrayInfo); } } } //--------------------------------------------------------------------------- MathLib::bigint 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 (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; 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)->type() != Token::eString) parameterLength = (*paramIter)->str().length(); handleNextParameter = true; break; case 's': if (paramIter != parameters.end() && *paramIter && (*paramIter)->type() == 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 = ""; i_d_x_f_found = false; percentCharFound = false; handleNextParameter = false; if (paramIter != parameters.end()) ++paramIter; } } return (MathLib::bigint)input_string_size; } void CheckBufferOverrun::checkSprintfCall(const Token *tok, const MathLib::bigint size) { if (size == 0) return; std::list parameters; const Token* vaArg = tok->tokAt(2)->nextArgument()->nextArgument(); while (vaArg) { if (Token::Match(vaArg->next(), "[,)]")) { if (vaArg->type() == Token::eString) parameters.push_back(vaArg); else if (vaArg->isNumber()) parameters.push_back(vaArg); else parameters.push_back(nullptr); } else // Parameter is more complex than just a value or variable. Ignore it for now and skip to next token. parameters.push_back(nullptr); vaArg = vaArg->nextArgument(); } MathLib::bigint len = countSprintfLength(tok->tokAt(2)->nextArgument()->strValue(), parameters); if (len > size) { bufferOverrunError(tok); } } //--------------------------------------------------------------------------- // 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 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 && tok != scope->classEnd; tok = tok->next()) { unsigned int dstVarId; unsigned int srcVarId; // Look for allocation of a buffer based on the size of a string if (Token::Match(tok, "%var% = malloc|g_malloc|g_try_malloc ( strlen ( %var% ) )")) { dstVarId = tok->varId(); srcVarId = tok->tokAt(6)->varId(); tok = tok->tokAt(8); } else if (Token::Match(tok, "%var% = new char [ strlen ( %var% ) ]")) { dstVarId = tok->varId(); srcVarId = tok->tokAt(7)->varId(); tok = tok->tokAt(9); } else if (Token::Match(tok, "%var% = realloc|g_realloc|g_try_realloc ( %var% , strlen ( %var% ) )")) { dstVarId = tok->varId(); srcVarId = tok->tokAt(8)->varId(); tok = tok->tokAt(10); } 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); } else if (Token::Match(tok, "sprintf ( %varid% , %str% , %var% )", dstVarId) && tok->tokAt(6)->varId() == srcVarId && tok->strAt(4).find("%s") != std::string::npos) { bufferOverrunError(tok); } } if (!tok) return; } } } //--------------------------------------------------------------------------- // memcpy(temp, "hello world", 50); //--------------------------------------------------------------------------- void CheckBufferOverrun::checkStringArgument() { const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 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, "%var% (") || !_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 std::list *minsizes = _settings->library.argminsizes(tok->str(), argnr); if (!minsizes) continue; if (checkMinSizes(*minsizes, tok, Token::getStrSize(argtok), nullptr)) 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 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]; Function * j = scope->function; if (j) { const Token* tok = j->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(); } if (varid == 0) continue; // Jump to the opening curly brace tok = tok->next()->link(); if (!Token::simpleMatch(tok, ") {")) continue; tok = tok->next(); // 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 ( %var% , * %varid%", varid) || Token::Match(tok, "strcpy|strcat ( %var% , %varid% [", varid)) { cmdLineArgsError(tok); } else if (Token::Match(tok, "sprintf ( %var% , %str% , %varid% [", varid) && tok->strAt(4).find("%s") != std::string::npos) { cmdLineArgsError(tok); } else if (Token::Match(tok, "sprintf ( %var% , %str% , * %varid%", varid) && tok->strAt(4).find("%s") != std::string::npos) { 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()); } void CheckBufferOverrun::negativeIndexError(const Token *tok, const ValueFlow::Value &index) { std::ostringstream ostr; ostr << "Array index " << index.intvalue << " is out of bounds."; if (index.condition) ostr << " Otherwise there is useless condition at line " << index.condition->linenr() << "."; reportError(tok, index.condition ? Severity::warning : Severity::error, "negativeIndex", ostr.str(), index.inconclusive); } CheckBufferOverrun::ArrayInfo::ArrayInfo() : _element_size(0), _declarationId(0) { } CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const Tokenizer *tokenizer, 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 = tokenizer->sizeOfType(var->typeEndToken()); else if (var->typeStartToken()->str() == "struct") _element_size = 100; else _element_size = tokenizer->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 { 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); } void CheckBufferOverrun::arrayIndexThenCheck() { if (!_settings->isEnabled("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 * const scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "%var% [ %var% ]")) { tok = tok->tokAt(2); unsigned int indexID = tok->varId(); if (!indexID) continue; const std::string& indexName(tok->str()); // skip array index.. tok = tok->tokAt(2); while (tok && tok->str() == "[") tok = tok->link()->next(); // syntax error if (!tok) return; // skip comparison if (tok->type() == Token::eComparisonOp) tok = tok->tokAt(2); // skip close parenthesis if (tok->str() == ")") tok = tok->next(); // check if array index is ok // statement can be closed in parentheses, so "(| " is using if (Token::Match(tok, "&& (| %varid% <|<=", indexID)) arrayIndexThenCheckError(tok, indexName); else if (Token::Match(tok, "&& (| %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."); } // ------------------------------------------------------------------------------------- // Check the second and the third parameter of the POSIX function write and validate // their values. // The parameters have the following meaning: // - 1.parameter: file descripter (not required for this check) // - 2.parameter: is a null terminated character string of the content to write. // - 3.parameter: the number of bytes to write. // // This check is triggered if the size of the string ( 2. parameter) is lower than // the number of bytes provided at the 3. parameter. // // References: // - http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html // - http://gd.tuwien.ac.at/languages/c/programming-bbrown/c_075.htm // - http://codewiki.wikidot.com/c:system-calls:write // ------------------------------------------------------------------------------------- void CheckBufferOverrun::writeOutsideBufferSize() { if (!_settings->standards.posix) 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 * const scope = symbolDatabase->functionScopes[i]; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "pwrite|write (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str% , %num%")) { const std::string & functionName(tok->str()); tok = tok->tokAt(2)->nextArgument(); // set tokenptr to %str% parameter const std::size_t stringLength = Token::getStrLength(tok)+1; // zero-terminated string! tok = tok->tokAt(2); // set tokenptr to %num% parameter const MathLib::bigint writeLength = MathLib::toLongNumber(tok->str()); if (static_cast(writeLength) > stringLength) writeOutsideBufferSizeError(tok, stringLength, writeLength, functionName); } } } } void CheckBufferOverrun::writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string &strFunctionName) { reportError(tok, Severity::error, "writeOutsideBufferSize", "Writing " + MathLib::toString(writeLength-stringLength) + " bytes outside buffer size.\n" "The number of bytes to write (" + MathLib::toString(writeLength) + " bytes) are bigger than the source buffer (" +MathLib::toString(stringLength)+ " bytes)." " Please check the second and the third parameter of the function '"+strFunctionName+"'."); } cppcheck-1.66/lib/checkbufferoverrun.h000066400000000000000000000275531236713773000200620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" #include "mathlib.h" #include #include #include 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()) { } /** This constructor is used when running checks. */ CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger); checkBufferOverrun.bufferOverrun(); checkBufferOverrun.bufferOverrun2(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.writeOutsideBufferSize(); } /** @brief %Check for buffer overruns */ void bufferOverrun(); /** @brief %Check for buffer overruns #2 (single pass, use ast and valueflow) */ void bufferOverrun2(); /** @brief Using array index before bounds check */ void arrayIndexThenCheck(); /** @brief %Check for buffer overruns by inspecting execution paths */ void executionPaths(); /** @brief %Check using POSIX write function and writing outside buffer size */ void writeOutsideBufferSize(); /** * @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::bigint countSprintfLength(const std::string &input_string, const std::list ¶meters); /** * @brief %Check code that matches: "sprintf ( %varid% , %str% [,)]" when varid is not 0, * and report found errors. * @param tok The "sprintf" token. * @param size The size of the buffer where sprintf is writing. */ void checkSprintfCall(const Token *tok, const MathLib::bigint size); /** 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 Tokenizer *tokenizer, 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; } }; /** Check for buffer overruns (based on ArrayInfo) */ void checkScope(const Token *tok, const ArrayInfo &arrayInfo); /** Check for buffer overruns */ void checkScope(const Token *tok, const std::vector &varname, const ArrayInfo &arrayInfo); /** Check readlink or readlinkat() buffer usage */ void checkReadlinkBufferUsage(const Token *ftok, const Token *scope_begin, const unsigned int varid, const MathLib::bigint total_size); /** * Helper function for checkFunctionCall - check a function parameter * \param tok token for the function name * \param par 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 &tok, const unsigned int par, 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 "%var% (" * @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); private: 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 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 std::string &object); // UB when result of calculation is out of bounds 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 possibleReadlinkBufferOverrunError(const Token *tok, const std::string &funcname, const std::string &varname); void argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname); void writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string& functionName); void valueFlowCheckArrayIndex(const Token * const tok, const ArrayInfo &arrayInfo); public: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckBufferOverrun c(0, settings, errorLogger); std::vector indexes; indexes.push_back(2); c.arrayIndexOutOfBoundsError(0, ArrayInfo(0, "array", 1, 2), indexes); c.bufferOverrunError(0, std::string("buffer")); c.strncatUsageError(0); c.outOfBoundsError(0, "index", true, 2, 1); c.sizeArgumentAsCharError(0); c.terminateStrncpyError(0, "buffer"); c.bufferNotZeroTerminatedError(0, "buffer", "strncpy"); c.negativeIndexError(0, -1); c.cmdLineArgsError(0); c.pointerOutOfBoundsError(0, "array"); c.arrayIndexThenCheckError(0, "index"); c.possibleBufferOverrunError(0, "source", "destination", false); c.possibleReadlinkBufferOverrunError(0, "readlink", "buffer"); c.argumentSizeError(0, "function", "array"); c.writeOutsideBufferSizeError(0,2,3,"write"); c.negativeMemoryAllocationSizeError(0); c.reportError(nullptr, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'x[10]' accessed at index 20, which is out of bounds. Otherwise condition 'y==20' is redundant."); } 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" "* Writing beyond bounds of a buffer\n" "* Allocating memory with a negative size\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkbufferoverrunH cppcheck-1.66/lib/checkclass.cpp000066400000000000000000003002511236713773000166150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "token.h" #include "errorlogger.h" #include "symboldatabase.h" #include #include #include //--------------------------------------------------------------------------- // Register CheckClass.. namespace { CheckClass instance; 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 ""; } inline 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 style = _settings->isEnabled("style"); const bool warnings = _settings->isEnabled("warning"); if (!style && !warnings) return; const std::size_t classes = symbolDatabase->classAndStructScopes.size(); for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; // There are no constructors. if (scope->numConstructors == 0 && style) { // 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 (!warnings) 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->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->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)) { bool stdtype = false; for (const Token *type = var->typeStartToken(); type && type->isName(); type = type->next()) { if (type->isStandardType()) { stdtype = true; break; } } if (!stdtype) { if (_settings->inconclusive) 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 (_settings->inconclusive) uninitVarError(func->token, scope->className, var->name(), true); } else uninitVarError(func->token, scope->className, var->name(), inconclusive); } } } } } } void CheckClass::copyconstructors() { if (!_settings->isEnabled("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) allocatedVars[tok->varId()] = tok; } } } } std::set copiedVars; const Token* copyCtor = 0; 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, "%var% (")) { if (allocatedVars.find(tok->varId()) != allocatedVars.end()) { if (tok->varId() && Token::Match(tok->tokAt(2), "%var% . %var% )")) 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% = %var% . %var% ;") && 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."); } 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."); } 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->type == Function::eCopyConstructor) && func->access == Public) publicCopy = true; else if (func->type == Function::eOperatorEqual && func->access == Public) publicAssign = true; } 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->type == Function::eCopyConstructor) && func->access == Public) publicCopy = true; else if ((func->type == Function::eMoveConstructor) && func->access == Public) publicMove = true; else if (func->type == Function::eOperatorEqual && func->access == Public) publicAssign = true; } return constructor && !(publicAssign || publicCopy || publicMove); } void CheckClass::assignVar(const std::string &varname, 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->name() == varname) { usage[count].assign = true; return; } } } void CheckClass::initVar(const std::string &varname, 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->name() == varname) { 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(0, "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, "%var% (")) { if (ftok->str() != func.name()) { initVar(ftok->str(), scope, usage); } else { // c++11 delegate constructor const Function *member = scope->findFunction(ftok); // 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, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), "%type%|,|{")) { if (ftok->str() != func.name()) { initVar(ftok->str(), scope, usage); } else { // c++11 delegate constructor const Function *member = scope->findFunction(ftok); // 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); } } } ftok = ftok->linkAt(1); } else if (level != 0 && Token::Match(ftok, "%var% =")) // assignment in the initializer: var(value = x) assignVar(ftok->str(), scope, usage); else if (ftok->str() == "(") level++; else if (ftok->str() == ")") level--; else if (ftok->str() == "{") { if (level == 0) initList = false; else ftok = ftok->link(); } } if (initList) continue; // Variable getting value from stream? if (Token::Match(ftok, ">> %var%")) { assignVar(ftok->strAt(1), scope, usage); } // Before a new statement there is "[{};()=[]" if (! Token::Match(ftok, "[{};()=[]")) 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, "[;{}] %var% (") && Token::Match(ftok->linkAt(2), ") . %var% ( *| this ) ;")) { assignAllVar(usage); break; } // Calling member variable function? if (Token::Match(ftok->next(), "%var% . %var% (")) { 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()->str(), scope, usage); break; } } ftok = ftok->tokAt(2); } if (!Token::Match(ftok->next(), "::| %var%") && !Token::Match(ftok->next(), "this . %var%") && !Token::Match(ftok->next(), "* %var% =") && !Token::Match(ftok->next(), "( * this ) . %var%")) 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, ":: %var%")) ftok = ftok->next(); while (Token::Match(ftok, "%var% ::")) ftok = ftok->tokAt(2); // Clearing all variables.. if (Token::Match(ftok, "::| memset ( this ,")) { assignAllVar(usage); return; } // Clearing array.. else if (Token::Match(ftok, "::| memset ( %var% ,")) { if (ftok->str() == "::") ftok = ftok->next(); assignVar(ftok->strAt(2), 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, "::| %var% (") && 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, "[(,] &| %var% [,)]")) { tok2 = tok2->next(); if (tok2->str() == "&") tok2 = tok2->next(); assignVar(tok2->str(), 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->str(), scope, usage); } } } } } // Assignment of member variable? else if (Token::Match(ftok, "%var% =")) { assignVar(ftok->str(), scope, usage); } // Assignment of array item of member variable? else if (Token::Match(ftok, "%var% [|.")) { const Token *tok2 = ftok; while (tok2) { if (tok2->strAt(1) == "[") tok2 = tok2->next()->link(); else if (Token::Match(tok2->next(), ". %var%")) tok2 = tok2->tokAt(2); else break; } if (tok2 && tok2->strAt(1) == "=") assignVar(ftok->str(), scope, usage); } // Assignment of array item of member variable? else if (Token::Match(ftok, "* %var% =")) { assignVar(ftok->next()->str(), scope, usage); } // The functions 'clear' and 'Clear' are supposed to initialize variable. if (Token::Match(ftok, "%var% . clear|Clear (")) { assignVar(ftok->str(), 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."); } 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.", 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='.", inconclusive); } //--------------------------------------------------------------------------- // ClassCheck: Use initialization list instead of assignment //--------------------------------------------------------------------------- void CheckClass::initializationListUsage() { if (!_settings->isEnabled("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, "%var% (")) // 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 (tok->varId() && Token::Match(tok, "%var% = %any%")) { const Variable* var = tok->variable(); if (var && var->scope() == owner && !var->isStatic()) { bool allowed = true; for (const Token* tok2 = tok->tokAt(2); tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { const Variable* var2 = tok2->variable(); if (var2 && var2->scope() == owner && tok2->strAt(-1)!=".") { // Is there a dependency between two member variables? allowed = false; break; } else if (var2 && (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, "%var% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called? allowed = false; break; } } if (!allowed) continue; if (!var->isPointer() && !var->isReference() && (var->type() || Token::Match(var->typeStartToken(), "std :: string|wstring !!::") || (Token::Match(var->typeStartToken(), "std :: %type% <") && !Token::simpleMatch(var->typeStartToken()->linkAt(3), "> ::")))) 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."); } //--------------------------------------------------------------------------- // ClassCheck: Unused private functions //--------------------------------------------------------------------------- static bool checkFunctionUsage(const std::string& name, 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, "%var% (")) { for (const Token *ftok = func->tokenDef->tokAt(2); ftok && ftok->str() != ")"; ftok = ftok->next()) { if (Token::Match(ftok, "= %var% [(,)]") && ftok->strAt(1) == 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->str() == name) // Function used. TODO: Handle overloads 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; } for (std::list::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->isClassOrStruct()) if (checkFunctionUsage(name, *i)) // Check nested classes, which can access private functions of their base return true; } return false; // Unused in this scope } void CheckClass::privateFunctions() { if (!_settings->isEnabled("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 privateFunctions; 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 privateFunctions.push_back(&*func); } // Bailout for overridden virtual functions of base classes if (!scope->definedType->derivedFrom.empty()) { // Check virtual functions for (std::list::iterator it = privateFunctions.begin(); it != privateFunctions.end();) { if ((*it)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes privateFunctions.erase(it++); else ++it; } } while (!privateFunctions.empty()) { const std::string& funcName = privateFunctions.front()->tokenDef->str(); // Check that all private functions are used bool used = checkFunctionUsage(funcName, &*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(funcName, it->type->classScope); else used = true; // Assume, it is used if we do not see friend class } if (!used) unusedPrivateFunctionError(privateFunctions.front()->tokenDef, scope->className, funcName); privateFunctions.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 + "'"); } //--------------------------------------------------------------------------- // 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 0; } void CheckClass::checkMemset() { 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 ( %any%")) { 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 derefs = 1; for (;; arg1 = arg1->next()) { if (arg1->str() == "&") derefs--; else if (arg1->str() == "*") derefs++; else break; } const Variable *var = arg1->variable(); if (var && arg1->strAt(1) == ",") { if (var->isPointer()) { derefs--; if (var->typeEndToken() && Token::simpleMatch(var->typeEndToken()->previous(), "* *")) // Check if it's a pointer to pointer derefs--; } if (var->isArray()) derefs -= (int)var->dimensions().size(); if (derefs == 0) type = var->typeScope(); // TODO: The SymbolDatabase mix up variables in nested structs. // So we must bailout right now if there are nested structs. bool bailout = false; for (const Token *typetok2 = var->typeStartToken(); typetok2 && typetok2 != var->typeEndToken(); typetok2 = typetok2->next()) { if (typetok2->str() == "::") bailout = true; else if (typetok2->str() == "{") { bailout = false; break; } } if (bailout) continue; } } // No type defined => The tokens didn't match if (!typeTok && !type) continue; if (typeTok && typeTok->str() == "(") typeTok = typeTok->next(); if (!type) { const Type* t = symbolDatabase->findVariableType(&(*scope), typeTok); if (t) type = t->classScope; } if (type) { std::list 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::list parsedTypes; checkMemsetType(&(*scope), tok->tokAt(2), tok->variable()->typeScope(), true, parsedTypes); if (tok->variable()->typeScope()->numConstructors > 0 && _settings->isEnabled("warning")) mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef); } } } } void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::list parsedTypes) { // If type has been checked there is no need to check it again if (std::find(parsedTypes.begin(), parsedTypes.end(), type) != parsedTypes.end()) return; parsedTypes.push_back(type); // 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()) { memsetErrorReference(tok, tok->str(), type->classDef->str()); continue; } // don't warn if variable static or const, pointer or reference if (!var->isStatic() && !var->isConst() && !var->isPointer()) { 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); } } } 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."); } 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."); } 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."); } void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type) { reportError(tok, Severity::error, "memsetClass", "Using '" + memfunc + "' on " + type + " that contains a reference."); } //--------------------------------------------------------------------------- // ClassCheck: "void operator=(" and "const type & operator=(" //--------------------------------------------------------------------------- void CheckClass::operatorEq() { if (!_settings->isEnabled("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 != Private) { // use definition for check so we don't have to deal with qualification if (!(Token::Match(func->retDef, "%type% &") && func->retDef->str() == scope->className)) { // make sure we really have a copy assignment operator if (Token::Match(func->tokenDef->tokAt(2), "const| %var% &")) { if (func->tokenDef->strAt(2) == "const" && func->tokenDef->strAt(3) == scope->className) operatorEqReturnError(func->retDef, scope->className); else if (func->tokenDef->strAt(2) == 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." ); } //--------------------------------------------------------------------------- // ClassCheck: "C& operator=(const C&) { ... return *this; }" // operator= should return a reference to *this //--------------------------------------------------------------------------- void CheckClass::operatorEqRetRefThis() { if (!_settings->isEnabled("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; for (; tok && tok != last; tok = tok->next()) { // check for return of reference to this if (tok->str() == "return") { 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) operatorEqRetRefThisError(func->token); } void CheckClass::operatorEqRetRefThisError(const Token *tok) { reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance."); } //--------------------------------------------------------------------------- // 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("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 = nullptr; 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(); // Check for assignment to the deleted pointer (only if its a member of the class) if (var && isMemberVar(scope, var)) { for (const Token *tok1 = var->next(); tok1 && (tok1 != last); tok1 = tok1->next()) { if (Token::Match(tok1, "%var% =")) { if (tok1->str() == var->str()) return true; } } } } return false; } bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs) { const Token *last = func->functionScope->classEnd; for (const Token *tok = func->functionScope->classStart; tok && tok != last; tok = tok->next()) { if (Token::simpleMatch(tok, "if (")) { const Token *tok1 = tok->tokAt(2); const Token *tok2 = tok->next()->link(); if (tok1 && tok2) { for (; tok1 && tok1 != tok2; tok1 = tok1->next()) { if (Token::Match(tok1, "this ==|!= & %var%")) { if (tok1->strAt(3) == rhs->str()) return true; } else if (Token::Match(tok1, "& %var% ==|!= this")) { if (tok1->strAt(1) == 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."); } //--------------------------------------------------------------------------- // 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 std::list inconclusive_errors; 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 (_settings->inconclusive) { 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) { inconclusive_errors.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 basepointer; for (std::size_t k = 0; k < symbolDatabase->getVariableListSize(); k++) { const Variable* var = symbolDatabase->getVariableFromVarId(k); if (var && var->isPointer() && var->type() == derivedFrom) basepointer.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% =") && tok->next()->varId() > 0 && basepointer.find(tok->next()->varId()) != basepointer.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% ;") && tok->next()->varId() && 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 *base_destructor = derivedFromScope->getDestructor(); const Token *base = nullptr; if (base_destructor) base = base_destructor->token; // Check that there is a destructor.. if (!base_destructor) { if (derivedFrom->derivedFrom.empty()) { virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false); // check for duplicate error and remove if if found std::list::iterator found = find(inconclusive_errors.begin(), inconclusive_errors.end(), base_destructor); if (found != inconclusive_errors.end()) inconclusive_errors.erase(found); } } else if (!base_destructor->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 (base_destructor->access == Public) { virtualDestructorError(base, derivedFrom->name(), derivedClass->str(), false); // check for duplicate error and remove if if found std::list::iterator found = find(inconclusive_errors.begin(), inconclusive_errors.end(), base_destructor); if (found != inconclusive_errors.end()) inconclusive_errors.erase(found); } } } } } } for (std::list::const_iterator i = inconclusive_errors.begin(); i != inconclusive_errors.end(); ++i) virtualDestructorError((*i)->tokenDef, (*i)->name(), "", true); } void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) { if (inconclusive) reportError(tok, Severity::warning, "virtualDestructor", "Class '" + Base + "' which has virtual members does not have a virtual destructor.", 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."); } //--------------------------------------------------------------------------- // warn for "this-x". The indented code may be "this->x" //--------------------------------------------------------------------------- void CheckClass::thisSubtraction() { if (!_settings->isEnabled("warning")) return; const Token *tok = _tokenizer->tokens(); for (;;) { tok = Token::findmatch(tok, "this - %var%"); 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 '->'?"); } //--------------------------------------------------------------------------- // can member function be const? //--------------------------------------------------------------------------- void CheckClass::checkConst() { // This is an inconclusive check. False positives: #3322. if (!_settings->inconclusive) return; if (!_settings->isEnabled("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 && !func->isFriend && !func->isStatic && !func->isVirtual) { // 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; 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 && opName[opName.size()-1] == '&') continue; } else { // don't warn for unknown types.. // LPVOID, HDC, etc if (previous->isUpperCaseName() && previous->str().size() > 2 && !symbolDatabase->isClassOrStruct(previous->str())) continue; } // check if base class function is virtual if (!scope->definedType->derivedFrom.empty()) { if (func->isImplicitlyVirtual(true)) continue; } bool memberAccessed = false; // if nothing non-const was found. write error.. if (checkConstFunc(&(*scope), &*func, memberAccessed)) { 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->isConst || (!memberAccessed && !func->isOperator)) { 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), "%var% . %var%")) { tok = tok->tokAt(-2); again = true; } else if (Token::Match(tok->tokAt(-2), "] . %var%")) { 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() && !scope->definedType->hasCircularDependencies()) { // 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; } 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; } static unsigned int countMinArgs(const Token* argList) { if (!argList) return 0; argList = argList->next(); if (argList->str() == ")") return 0; unsigned int count = 1; for (; argList; argList = argList->next()) { if (argList->link() && Token::Match(argList, "(|[|{|<")) argList = argList->link(); else if (argList->str() == ",") count++; else if (argList->str() == "=") return count-1; else if (argList->str() == ")") break; } return count; } bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) const { unsigned int args = countParameters(tok); for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { /** @todo we need to look at the argument types when there are overloaded functions * with the same number of arguments */ if (func->tokenDef->str() == tok->str() && (func->argCount() == args || (func->argCount() > args && countMinArgs(func->argDef) <= args))) { return !func->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 && !derivedFrom->hasCircularDependencies()) { if (isMemberFunc(derivedFrom->classScope, tok)) return true; } } } return false; } bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) const { unsigned int args = countParameters(tok); std::list::const_iterator func; unsigned int matches = 0; unsigned int consts = 0; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { /** @todo we need to look at the argument types when there are overloaded functions * with the same number of arguments */ if (func->tokenDef->str() == tok->str() && (func->argCount() == args || (func->argCount() > args && countMinArgs(func->argDef) <= args))) { matches++; if (func->isConst) consts++; } } // if there are multiple matches that are all const, return const if (matches > 0 && matches == consts) return true; // 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 && !derivedFrom->hasCircularDependencies()) { if (isConstMemberFunc(derivedFrom->classScope, tok)) return true; } } } return false; } 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* jumpBackToken = 0; const Token *lastVarTok = tok1; const Token *end = tok1; for (;;) { if (Token::Match(end->next(), ". %var%")) { end = end->tokAt(2); if (end->varId()) lastVarTok = end; } else if (end->strAt(1) == "[") { if (end->varId()) { const Variable *var = end->variable(); // The container contains the STL types whose operator[] is not a const. // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_containers [] = { "map", "unordered_map" }; if (var && var->isStlType(stl_containers)) 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()->type() == 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()->type() == Token::eIncDecOp || tok1->previous()->type() == 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, "%var% (") && !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 for (const Token* tok2 = tok1->tokAt(2); 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, 0, 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?", 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?", true); } //--------------------------------------------------------------------------- // ClassCheck: Check that initializer list is in declared order. //--------------------------------------------------------------------------- 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("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 * info = symbolDatabase->classAndStructScopes[i]; std::list::const_iterator func; // iterate through all member functions looking for constructors for (func = info->functionList.begin(); func != info->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->str() != "{") { if (Token::Match(tok, "%var% (")) { const Variable *var = info->getVariable(tok->str()); if (var) vars.push_back(VarInfo(var, tok)); if (Token::Match(tok->tokAt(2), "%var% =")) { var = info->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(), info->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.", true); } void CheckClass::checkPureVirtualFunctionCall() { 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 == 0 || !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()); } } } void CheckClass::checkDuplInheritedMembers() { if (!_settings->isEnabled("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); } 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; } } 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."); } cppcheck-1.66/lib/checkclass.h000066400000000000000000000322211236713773000162610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" class Scope; class Function; /// @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(NULL) { } /** @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(); } /** @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.virtualDestructor(); checkClass.checkConst(); checkClass.copyconstructors(); checkClass.checkPureVirtualFunctionCall(); checkClass.checkDuplInheritedMembers(); } /** @brief %Check that all class constructors are ok */ void constructors(); /** @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::list 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(); void initializationListUsage(); void copyconstructors(); /** @brief call of pure virtual funcion */ void checkPureVirtualFunctionCall(); /** @brief Check duplicated inherited members */ void checkDuplInheritedMembers(); private: const SymbolDatabase *symbolDatabase; // Reporting errors.. void noConstructorError(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 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 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 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckClass c(0, settings, errorLogger); c.noConstructorError(0, "classname", false); //c.copyConstructorMallocError(0, 0, "var"); c.copyConstructorShallowCopyError(0, "var"); c.noCopyConstructorError(0, "class", false); c.uninitVarError(0, "classname", "varname", false); c.operatorEqVarError(0, "classname", "", false); c.unusedPrivateFunctionError(0, "classname", "funcname"); c.memsetError(0, "memfunc", "classname", "class"); c.mallocOnClassWarning(0, "malloc", 0); c.mallocOnClassError(0, "malloc", 0, "std::string"); c.operatorEqReturnError(0, "class"); c.virtualDestructorError(0, "Base", "Derived", false); c.thisSubtractionError(0); c.operatorEqRetRefThisError(0); c.operatorEqToSelfError(0); c.checkConstError(0, "class", "function", false); c.checkConstError(0, "class", "function", true); c.initializerListError(0, 0, "class", "variable"); c.suggestInitializationList(0, "variable"); c.duplInheritedMembersError(0, 0, "class", "class", "variable", false, false); } 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" "* 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" "* Suspicious subtraction from 'this'\n" "* Call of pure virtual function in constructor/destructor\n" "* Duplicated inherited data members\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 varname name of variable to mark assigned * @param scope pointer to variable Scope * @param usage reference to usage vector */ static void assignVar(const std::string &varname, const Scope *scope, std::vector &usage); /** * @brief initialize a variable in the varlist * @param varname name of variable to mark initialized * @param scope pointer to variable Scope * @param usage reference to usage vector */ static void initVar(const std::string &varname, 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.66/lib/checkexceptionsafety.cpp000066400000000000000000000275261236713773000207350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "symboldatabase.h" //--------------------------------------------------------------------------- // Register CheckExceptionSafety.. namespace { CheckExceptionSafety instance; } //--------------------------------------------------------------------------- void CheckExceptionSafety::destructors() { if (!_settings->isEnabled("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 execptions 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 if (tok->str() == "throw") { destructorsError(tok, scope->className); break; } } } } } } void CheckExceptionSafety::deallocThrow() { if (!_settings->isEnabled("warning")) return; 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) 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 (_settings->inconclusive) { // 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("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(); 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("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 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) nothrowThrowError(throws); } // check __attribute__((nothrow)) functions else if (function->isAttributeNothrow()) { const Token *throws = functionThrows(function); if (throws) nothrowAttributeThrowError(throws); } // check __declspec(nothrow) functions else if (function->isDeclspecNothrow()) { const Token *throws = functionThrows(function); if (throws) nothrowDeclspecThrowError(throws); } } } //-------------------------------------------------------------------------- // void func() { functionWithExceptionSpecification(); } //-------------------------------------------------------------------------- void CheckExceptionSafety::unhandledExceptionSpecification() { if (!_settings->isEnabled("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.66/lib/checkexceptionsafety.h000066400000000000000000000177031236713773000203760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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."); } 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."); } 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;'."); } 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++."); } /** Don't throw exceptions in noexcept functions */ void noexceptThrowError(const Token * const tok) { reportError(tok, Severity::error, "exceptThrowInNoexecptFunction", "Exception thrown in noexcept function."); } /** Don't throw exceptions in throw() functions */ void nothrowThrowError(const Token * const tok) { reportError(tok, Severity::error, "exceptThrowInNoThrowFunction", "Exception thrown in throw() function."); } /** Don't throw exceptions in __attribute__((nothrow)) functions */ void nothrowAttributeThrowError(const Token * const tok) { reportError(tok, Severity::error, "exceptThrowInAttributeNoThrowFunction", "Exception thrown in __attribute__((nothrow)) function."); } /** Don't throw exceptions in __declspec(nothrow) functions */ void nothrowDeclspecThrowError(const Token * const tok) { reportError(tok, Severity::error, "exceptThrowInDeclspecNoThrowFunction", "Exception thrown in __declspec(nothrow) function."); } /** Missing exception specification */ void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) { std::string str1(tok1 ? tok1->str() : "foo"); std::list locationList; locationList.push_back(tok1); locationList.push_back(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.", true); } /** Generate all possible errors (for --errorlist) */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckExceptionSafety c(0, settings, errorLogger); c.destructorsError(0, "Class"); c.deallocThrowError(0, "p"); c.rethrowCopyError(0, "varname"); c.catchExceptionByValueError(0); c.noexceptThrowError(0); c.nothrowThrowError(0); c.nothrowAttributeThrowError(0); c.nothrowDeclspecThrowError(0); c.unhandledExceptionSpecificationError(0, 0, "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 function\n" "* Throwing exception in nothrow() function\n" "* Throwing exception in __attribute__((nothrow)) function\n" "* Throwing exception in __declspec(nothrow) function\n" "* Unhandled exception specification when calling function foo()\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkexceptionsafetyH cppcheck-1.66/lib/checkinternal.cpp000066400000000000000000000310771236713773000173330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "symboldatabase.h" #include #include #include #include // Register this check class (by creating a static instance of it). // Disabled in release builds namespace { CheckInternal instance; } void CheckInternal::checkTokenMatchPatterns() { for (const Token *tok = _tokenizer->tokens(); tok; 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 *pattern_tok = tok->tokAt(4)->nextArgument(); if (!pattern_tok || pattern_tok->type() != Token::eString) continue; const std::string pattern = pattern_tok->strValue(); if (pattern.empty()) { simplePatternError(tok, pattern, funcname); continue; } const char *p = pattern.c_str(); while (*p) { while (*p && std::isspace(*p)) p++; const char *start = p; while (*p && !std::isspace(*p)) p++; const char *end = p - 1; if (start < end && !(*start == '[' && *end == ']')) { bool cmd = (*start=='%' && std::isalpha(*(start+1))); // check multicompare pattern.. for (const char *s = start; s != end; s++) { if (*s == '|') { if (!(*(s+1) == '%' && std::isalpha(*(s+2)))) { cmd = false; } else if (!cmd && std::strncmp(s+1,"%op%",4)!=0 && std::strncmp(s+1,"%or%",4)!=0 && std::strncmp(s+1,"%cop%",5)!=0 && std::strncmp(s+1,"%var%",5)!=0 && std::strncmp(s+1,"%oror%",6)!=0) { multiComparePatternError(tok, pattern, funcname); } } } } } 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; simplePatternError(tok, pattern, funcname); } } void CheckInternal::checkTokenSimpleMatchPatterns() { for (const Token *tok = _tokenizer->tokens(); tok; 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 *pattern_tok = tok->tokAt(4)->nextArgument(); if (!pattern_tok || pattern_tok->type() != Token::eString) continue; const std::string pattern = pattern_tok->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) { 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 i = 0; i < pattern.length() - 1; i++) { if (pattern[i] == '%' && pattern[i + 1] != ' ') complexPatternError(tok, pattern, funcname); else if (pattern[i] == '!' && pattern[i + 1] == '!') complexPatternError(tok, pattern, funcname); } } } } void CheckInternal::checkMissingPercentCharacter() { static std::set magics; if (magics.empty()) { magics.insert("%any%"); magics.insert("%bool%"); magics.insert("%char%"); magics.insert("%comp%"); magics.insert("%num%"); magics.insert("%op%"); magics.insert("%cop%"); magics.insert("%or%"); magics.insert("%oror%"); magics.insert("%str%"); magics.insert("%type%"); magics.insert("%var%"); magics.insert("%varid%"); } for (const Token *tok = _tokenizer->tokens(); tok; 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 *pattern_tok = tok->tokAt(4)->nextArgument(); if (!pattern_tok || pattern_tok->type() != Token::eString) continue; const std::string pattern = pattern_tok->strValue(); std::set::const_iterator magic, magics_end = magics.end(); for (magic = magics.begin(); magic != magics_end; ++magic) { const std::string broken_magic = (*magic).substr(0, (*magic).size()-1); std::string::size_type pos = 0; while ((pos = pattern.find(broken_magic, pos)) != std::string::npos) { // Check if it's the full pattern if (pattern.find(*magic, pos) != pos) { // Known whitelist of substrings if ((broken_magic == "%var" && pattern.find("%varid%", pos) == pos) || (broken_magic == "%or" && pattern.find("%oror%", pos) == pos)) { ++pos; continue; } missingPercentCharacterError(tok, pattern, funcname); } ++pos; } } } } void CheckInternal::checkUnknownPattern() { static std::set knownPatterns; if (knownPatterns.empty()) { knownPatterns.insert("%any%"); knownPatterns.insert("%bool%"); knownPatterns.insert("%char%"); knownPatterns.insert("%comp%"); knownPatterns.insert("%num%"); knownPatterns.insert("%op%"); knownPatterns.insert("%cop%"); knownPatterns.insert("%or%"); knownPatterns.insert("%oror%"); knownPatterns.insert("%str%"); knownPatterns.insert("%type%"); knownPatterns.insert("%var%"); knownPatterns.insert("%varid%"); } for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; // Get pattern string const Token *pattern_tok = tok->tokAt(4)->nextArgument(); if (!pattern_tok || pattern_tok->type() != Token::eString) continue; const std::string pattern = pattern_tok->strValue(); bool inBrackets = false; for (std::string::size_type i = 0; i < pattern.length()-1; i++) { if (pattern[i] == '[' && (i == 0 || pattern[i-1] == ' ')) inBrackets = true; else if (pattern[i] == ']') inBrackets = false; else if (pattern[i] == '%' && pattern[i+1] != ' ' && pattern[i+1] != '|' && !inBrackets) { std::string::size_type end = pattern.find('%', i+1); if (end != std::string::npos) { std::string s = pattern.substr(i, end-i+1); if (knownPatterns.find(s) == knownPatterns.end()) unknownPatternError(tok, s); } } } } } void CheckInternal::checkRedundantNextPrevious() { for (const Token *tok = _tokenizer->tokens(); tok; 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(2), ") . previous|next|tokAt|strAt|linkAt|str|link ("))) { const std::string& func1 = tok->strAt(1); const std::string& func2 = tok->linkAt(2)->strAt(2); if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->linkAt(2)->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->strAt(1); const std::string& func2 = tok->strAt(9); if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->strAt(11) != ")") continue; redundantNextPreviousError(tok, func1, func2); } } } 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%,%var%,%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%\"."); } #endif // #ifdef CHECK_INTERNAL cppcheck-1.66/lib/checkinternal.h000066400000000000000000000104331236713773000167710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" /// @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("internal")) return; CheckInternal checkInternal(tokenizer, settings, errorLogger); checkInternal.checkTokenMatchPatterns(); checkInternal.checkTokenSimpleMatchPatterns(); checkInternal.checkMissingPercentCharacter(); checkInternal.checkUnknownPattern(); checkInternal.checkRedundantNextPrevious(); } /** @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(); 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckInternal c(0, settings, errorLogger); c.multiComparePatternError(0, ";|%type%", "Match"); c.simplePatternError(0, "class {", "Match"); c.complexPatternError(0, "%type% ( )", "Match"); c.missingPercentCharacterError(0, "%num", "Match"); c.unknownPatternError(0, "%typ"); c.redundantNextPreviousError(0, "previous", "next"); c.orInComplexPattern(0, "||", "Match"); } 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.66/lib/checkio.cpp000066400000000000000000003362121236713773000161250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "symboldatabase.h" #include #include //--------------------------------------------------------------------------- // Register CheckIO.. namespace { CheckIO instance; } //--------------------------------------------------------------------------- // std::cout << std::cout; //--------------------------------------------------------------------------- void CheckIO::checkCoutCerrMisusage() { 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 + "'."); } //--------------------------------------------------------------------------- // 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; Filepointer(OpenMode mode_ = UNKNOWN_OM) : mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0), append_mode(UNKNOWN_AM) { } }; void CheckIO::checkFileUsage() { static const char* _whitelist[] = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; static const std::set whitelist(_whitelist, _whitelist + sizeof(_whitelist)/sizeof(*_whitelist)); const bool windows = _settings->isWindowsPlatform(); 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") { // 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 (tok->varId() && 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, "%var% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { std::string mode; const Token* fileTok = 0; 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->type() == 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 ( & %var%")) { const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); if (modeTok && modeTok->type() == 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")) { if (Token::simpleMatch(tok, "fflush ( stdin )")) fflushOnInputStreamError(tok, tok->strAt(2)); else { fileTok = tok->tokAt(2); 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")) { const Token* const end2 = tok->linkAt(1); 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, "%var% .")) fileTok = fileTok->tokAt(2); if (!fileTok || !fileTok->varId()) 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; } f.mode_indent = indent; break; case Filepointer::POSITIONING: if (f.mode == CLOSED) useClosedFileError(tok); else if (f.append_mode == Filepointer::APPEND && tok->str() != "fflush" && _settings->isEnabled("warning")) 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::error, "fflushOnInputStream", "fflush() called on input stream '" + varname + "' results in undefined behaviour."); } 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."); } void CheckIO::readWriteOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "readWriteOnlyFile", "Read operation on a file that was opened only for writing."); } void CheckIO::writeReadOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "writeReadOnlyFile", "Write operation on a file that was opened only for reading."); } void CheckIO::useClosedFileError(const Token *tok) { reportError(tok, Severity::error, "useClosedFile", "Used file that is not opened."); } void CheckIO::seekOnAppendedFileError(const Token *tok) { reportError(tok, Severity::warning, "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect."); } //--------------------------------------------------------------------------- // scanf without field width limits can crash with huge input data //--------------------------------------------------------------------------- void CheckIO::invalidScanf() { const bool warning = _settings->isEnabled("warning"); const bool portability = _settings->isEnabled("portability"); if (!warning && !portability) return; const bool windows = _settings->isWindowsPlatform(); 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->type() == 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 (warning && (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, false); else if (portability && formatstr[i] != 'n' && formatstr[i] != 'c' && !windows) invalidScanfError(tok, true); // Warn about libc bug in versions prior to 2.13-25 format = false; } } } } } void CheckIO::invalidScanfError(const Token *tok, bool portability) { if (portability) reportError(tok, Severity::portability, "invalidscanf", "scanf without field width limits can crash with huge input data on some versions of libc.\n" "scanf without field width limits can crash with huge input data on libc versions older than 2.13-25. Add a field " "width specifier to fix this problem:\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"); else 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. Add a field width " "specifier to fix this problem:\n" " %s => %20s\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" "To make it crash, type in more than 5 characters."); } //--------------------------------------------------------------------------- // 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(); *formatStringTok = nullptr; if (argTok->variable()) { const Token *varTok = argTok->variable()->nameToken(); if (Token::Match(varTok, "%var% ; %var% = %str% ;") && varTok->str() == varTok->strAt(2) && Token::Match(varTok->tokAt(-4), "const char|wchar_t * const")) { *formatStringTok = varTok->tokAt(4); } else if (Token::Match(varTok, "%var% [ %num% ] = %str% ;") && Token::Match(varTok->tokAt(-2), "const char|wchar_t")) { *formatStringTok = varTok->tokAt(5); } } return true; } return false; } // Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename 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 warning = _settings->isEnabled("warning"); const bool windows = _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 = 0; // Points to first va_list argument const Token* formatStringTok = 0; // Points to format string token std::string formatString; bool scan = false; bool scanf_s = false; int formatStringArgNo = -1; if (Token::Match(tok->next(), "( %any%") && _settings->library.formatstr_function(tok->str())) { const std::map& argumentChecks = _settings->library.argumentChecks.at(tok->str()); for (std::map::const_iterator i = argumentChecks.begin(); i != argumentChecks.end(); ++i) { if (i->second.formatstr) { formatStringArgNo = i->first - 1; break; } } scan = _settings->library.formatstr_scan(tok->str()); scanf_s = _settings->library.formatstr_secure(tok->str()); } if (formatStringArgNo >= 0) { // formatstring found in library. Find format string and first argument belonging to format string. if (!findFormat(formatStringArgNo, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else if (windows && Token::Match(tok, "Format|AppendFormat (") && Token::Match(tok->tokAt(-2), "%var% .") && tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->typeStartToken()->str() == "CString") { // Find second parameter and format string if (!findFormat(0, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else if (Token::simpleMatch(tok, "swprintf (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) { // Find third parameter and format string if (!findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else if (Token::simpleMatch(tok, "swprintf (") && !Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) { // Find forth parameter and format string if (!findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else if (windows && 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 (windows && 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) formatString = formatStringTok->str(); else continue; // 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::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); 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); } } if (argListTok && argListTok->type() != 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 (scanf_s) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } done = true; break; case 'x': case 'X': case 'o': specifier += *i; if (argInfo.typeToken->type() == 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.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(), "intmax_t", "u")) 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(), "intmax_t", "u")) 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(), "ptrdiff_t") && !typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'j': if (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); 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(), "intmax_t", "u")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; } } } done = true; break; case 'n': case 'd': case 'i': specifier += *i; if (argInfo.typeToken->type() == 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") || 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") || 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 (argInfo.typeToken->originalName() != "intmax_t") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") && !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 'u': specifier += *i; if (argInfo.typeToken->type() == 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") || 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") || 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 (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") || 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(), "ssize_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; } } } done = true; break; case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': specifier += *i; if (argInfo.typeToken->type() == 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 (specifier[1] == 'l') { if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else 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 (!scan && warning) { std::string specifier; bool done = false; while (!done) { switch (*i) { case 's': if (argListTok->type() != 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->type() == Token::eString) invalidPrintfArgTypeError_n(tok, numFormat, &argInfo); done = true; break; case 'c': case 'x': case 'X': case 'o': specifier += *i; if (argInfo.typeToken->type() == Token::eString) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); else if (argInfo.isKnownType()) { if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } else if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); break; case 'j': if (!(argInfo.typeToken->originalName() == "intmax_t" || argInfo.typeToken->originalName() == "uintmax_t")) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "WPARAM" || argInfo.typeToken->originalName() == "UINT_PTR" || argInfo.typeToken->originalName() == "LONG_PTR" || argInfo.typeToken->originalName() == "LPARAM" || argInfo.typeToken->originalName() == "LRESULT")) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int")) invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); break; } } } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo); } done = true; break; case 'd': case 'i': specifier += *i; if (argInfo.typeToken->type() == Token::eString) { invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else 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 '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_uint(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; } } } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } done = true; break; case 'u': specifier += *i; if (argInfo.typeToken->type() == Token::eString) { invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!argInfo.typeToken->isUnsigned() && argInfo.typeToken->str() != "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 '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 '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; 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; } } } else if (argInfo.isArrayOrPointer() && !argInfo.element) { invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } done = true; break; case 'p': if (argInfo.typeToken->type() == Token::eString) invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); 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->type() == Token::eString) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); else if (argInfo.isKnownType()) { if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else 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); } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays 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 (warning) { // 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 * tok, const Settings *settings) : variableInfo(0) , typeToken(0) , functionInfo(0) , element(false) , _template(false) , address(false) , tempToken(0) { if (tok) { if (tok->type() == Token::eString) { typeToken = tok; return; } else if (tok->str() == "&" || tok->type() == Token::eVariable || tok->type() == Token::eFunction || Token::Match(tok, "%type% ::") || (Token::Match(tok, "static_cast|reinterpret_cast|const_cast <") && Token::simpleMatch(tok->linkAt(1), "> (") && Token::Match(tok->linkAt(1)->linkAt(1), ") ,|)"))) { if (Token::Match(tok, "static_cast|reinterpret_cast|const_cast")) { typeToken = tok->tokAt(2); while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); return; } if (tok->str() == "&") { address = true; tok = tok->next(); } while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); if (!tok || !(tok->type() == Token::eVariable || tok->type() == Token::eFunction)) return; const Token *varTok = nullptr; const Token *tok1 = tok->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()->type() == Token::eFunction) { const Function * function = varTok->link()->previous()->function(); if (function && function->retDef) { typeToken = function->retDef; while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); functionInfo = function; element = true; return; } else return; } } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->type() == Token::eFunction) { const Function * function = tok1->linkAt(-1)->previous()->function(); if (function && function->retDef) { typeToken = function->retDef; while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); functionInfo = function; element = false; return; } else 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 ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( )") && isStdContainer(tok1->previous())) || (Token::Match(tok1->previous(), "] . size|empty|c_str ( )") && Token::Match(tok1->previous()->link()->previous(), "%var%") && isStdContainer(tok1->previous()->link()->previous()))) { tempToken = new Token(0); 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 = 0; typeToken = 0; } return; } else if (!(tok1->str() == "." || tok1->type() == Token::eVariable || tok1->type() == 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 typeToken = variableInfo->typeStartToken(); } return; } } } } CheckIO::ArgumentInfo::~ArgumentInfo() { if (tempToken) { while (tempToken->next()) tempToken->deleteNext(); delete tempToken; } } bool CheckIO::ArgumentInfo::isStdVectorOrString() { // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_vector[] = { "array", "vector" }; // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_string[] = { "string", "u16string", "u32string", "wstring" }; if (variableInfo->isStlType(stl_vector)) { typeToken = variableInfo->typeStartToken()->tokAt(4); _template = true; return true; } else if (variableInfo->isStlType(stl_string)) { tempToken = new Token(0); 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()) { for (std::size_t i = 0, e = variableInfo->type()->derivedFrom.size(); i != e; ++i) { if (Token::Match(variableInfo->type()->derivedFrom[i].nameTok, "std :: vector|array <")) { typeToken = variableInfo->type()->derivedFrom[i].nameTok->tokAt(4); _template = true; return true; } else if (Token::Match(variableInfo->type()->derivedFrom[i].nameTok, "std :: string|wstring")) { tempToken = new Token(0); tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex()); tempToken->linenr(variableInfo->typeStartToken()->linenr()); if (variableInfo->type()->derivedFrom[i].nameTok->strAt(2) == "string") tempToken->str("char"); else tempToken->str("wchar_t"); typeToken = tempToken; return true; } } } return false; } bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok) { // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_container[] = { "array", "bitset", "deque", "forward_list", "hash_map", "hash_multimap", "hash_set", "list", "map", "multimap", "multiset", "priority_queue", "queue", "set", "stack", "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector" }; // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_string[]= { "string", "u16string", "u32string", "wstring" }; 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, e = derivedFrom.size(); i != e; ++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 (tok && 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); static std::set knownTypes; if (knownTypes.empty()) { knownTypes.insert("string"); knownTypes.insert("wstring"); } const Token* varTypeTok = typeToken; if (varTypeTok->str() == "std") varTypeTok = varTypeTok->tokAt(2); return ((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (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("style")) 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()); } void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, unsigned int index, unsigned int numFunction) { 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()); } void CheckIO::invalidScanfArgTypeError_s(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { 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::warning, "invalidScanfArgType_s", errmsg.str()); } void CheckIO::invalidScanfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) { 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 << "ptrdiff_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::warning, "invalidScanfArgType_int", errmsg.str()); } void CheckIO::invalidScanfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { 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::warning, "invalidScanfArgType_float", errmsg.str()); } void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) { std::ostringstream errmsg; errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str()); } void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) { std::ostringstream errmsg; errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str()); } void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) { 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::warning, "invalidPrintfArgType_p", errmsg.str()); } 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.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_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { 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::warning, "invalidPrintfArgType_int", errmsg.str()); } void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { 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::warning, "invalidPrintfArgType_uint", errmsg.str()); } void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { 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::warning, "invalidPrintfArgType_sint", errmsg.str()); } void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { 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::warning, "invalidPrintfArgType_float", errmsg.str()); } void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) { if (argInfo) { os << "\'"; const Token *type = argInfo->typeToken; if (type->type() == 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); 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->originalName() == "__int64" || type->originalName() == "__int32") && type->isUnsigned()) os << "unsigned "; os << type->originalName(); if (type->strAt(1) == "*" || argInfo->address) os << " *"; os << " {aka "; type->stringify(os, false, true); 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) { 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()); } void CheckIO::invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var) { std::ostringstream errmsg; Severity::SeverityType severity = Severity::warning; bool inconclusive = false; if (var) { if (var->dimension(0) > width) { if (!_settings->inconclusive) return; inconclusive = true; errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer" << " '" << var->name() << "[" << var->dimension(0) << "]'."; } else { errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '" << var->name() << "[" << var->dimension(0) << "]', use %" << (var->dimension(0) - 1) << "s to prevent overflowing it."; severity = Severity::error; } } else errmsg << "Width " << width << " given in format string (no. " << numFormat << ") doesn't match destination buffer."; if (severity == Severity::error || _settings->isEnabled("style")) reportError(tok, severity, "invalidScanfFormatWidth", errmsg.str(), inconclusive); } cppcheck-1.66/lib/checkio.h000066400000000000000000000174051236713773000155720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" /// @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); ~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; bool element; bool _template; bool address; Token *tempToken; private: ArgumentInfo(const ArgumentInfo &); // not implemented ArgumentInfo operator = (const ArgumentInfo &); // not implemented }; // 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, bool portability); void wrongPrintfScanfArgumentsError(const Token* tok, const std::string &function, 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_int(const Token* tok, unsigned int numFormat, const std::string& specifier, 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); static void argumentType(std::ostream & s, const ArgumentInfo * argInfo); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckIO c(0, settings, errorLogger); c.coutCerrMisusageError(0, "cout"); c.fflushOnInputStreamError(0, "stdin"); c.ioWithoutPositioningError(0); c.readWriteOnlyFileError(0); c.writeReadOnlyFileError(0); c.useClosedFileError(0); c.seekOnAppendedFileError(0); c.invalidScanfError(0, false); c.wrongPrintfScanfArgumentsError(0,"printf",3,2); c.invalidScanfArgTypeError_s(0, 1, "s", NULL); c.invalidScanfArgTypeError_int(0, 1, "d", NULL, false); c.invalidScanfArgTypeError_float(0, 1, "f", NULL); c.invalidPrintfArgTypeError_s(0, 1, NULL); c.invalidPrintfArgTypeError_n(0, 1, NULL); c.invalidPrintfArgTypeError_p(0, 1, NULL); c.invalidPrintfArgTypeError_int(0, 1, "X", NULL); c.invalidPrintfArgTypeError_uint(0, 1, "u", NULL); c.invalidPrintfArgTypeError_sint(0, 1, "i", NULL); c.invalidPrintfArgTypeError_float(0, 1, "f", NULL); c.invalidScanfFormatWidthError(0, 10, 5, NULL); c.wrongPrintfScanfPosixParameterPositionError(0, "printf", 2, 1); } static std::string myName() { return "IO"; } std::string classInfo() const { return "Check 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.66/lib/checkleakautovar.cpp000066400000000000000000000474111236713773000200340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak #include "checkother.h" // <- doubleFreeError #include "tokenize.h" #include "symboldatabase.h" #include //--------------------------------------------------------------------------- const int DEALLOC = -1; const int NOALLOC = 0; // Register this check class (by creating a static instance of it) namespace { CheckLeakAutoVar instance; } //--------------------------------------------------------------------------- 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::cout << "alloctype='" << it->second << "' " << "possibleUsage='" << strusage << "'" << 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"); } void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName) { if (_settings->checkLibrary && _settings->isEnabled("information")) { reportError(tok, Severity::information, "checkLibraryUseIgnore", "--check-library: Function " + functionName + "() should have / configuration"); } } void CheckLeakAutoVar::check() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // 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]; // Empty variable info VarInfo varInfo; // Local variables that are known to be non-zero. const std::set notzero; 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); } } 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()) { // Deallocation and then dereferencing pointer.. if (tok->varId() > 0) { const std::map::iterator var = alloctype.find(tok->varId()); if (var != alloctype.end()) { if (var->second == DEALLOC && !Token::Match(tok->previous(), "[;{},=] %var% =")) { 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(), "& %var% = %var% ;")) { varInfo->referenced.insert(tok->tokAt(2)->varId()); } } if (tok->str() == "(" && tok->previous()->isName()) { functionCall(tok->previous(), varInfo, NOALLOC); 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 // assignment.. if (tok->varId() && Token::Match(tok, "%var% =")) { // taking address of another variable.. if (Token::Match(tok->next(), "= %var% [+;]")) { if (tok->tokAt(2)->varId() != tok->varId()) { // If variable points at allocated memory => error leakIfAllocated(tok, *varInfo); // no multivariable checking currently => bail out for rhs variables for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { break; } if (tok2->varId()) { varInfo->erase(tok2->varId()); } } } } // is variable used in rhs? bool used_in_rhs = false; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { break; } if (tok->varId() == tok2->varId()) { used_in_rhs = true; break; } } // TODO: Better checking how the pointer is used in rhs? if (used_in_rhs) continue; // Variable has already been allocated => error if (conditionalAlloc.find(tok->varId()) == conditionalAlloc.end()) leakIfAllocated(tok, *varInfo); varInfo->erase(tok->varId()); // not a local variable nor argument? const Variable *var = tok->variable(); if (var && !var->isArgument() && (!var->isLocal() || var->isStatic())) { continue; } // Don't check reference variables if (var && var->isReference()) continue; // allocation? if (Token::Match(tok->tokAt(2), "%type% (")) { int i = _settings->library.alloc(tok->tokAt(2)); if (i > 0) { alloctype[tok->varId()] = i; } } // Assigning non-zero value variable. It might be used to // track the execution for a later if condition. if (Token::Match(tok->tokAt(2), "%num% ;") && MathLib::toLongNumber(tok->strAt(2)) != 0) notzero.insert(tok->varId()); else if (Token::Match(tok->tokAt(2), "- %type% ;") && tok->tokAt(3)->isUpperCaseName()) notzero.insert(tok->varId()); else notzero.erase(tok->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 (innerTok->str() == ")") break; if (innerTok->str() == "(" && innerTok->previous()->isName()) { const int deallocId = _settings->library.dealloc(tok); functionCall(innerTok->previous(), varInfo, deallocId); 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 if (Token::Match(tok->next(), "( %var% )")) { varInfo2.erase(tok->tokAt(2)->varId()); if (notzero.find(tok->tokAt(2)->varId()) != notzero.end()) varInfo2.clear(); } else if (Token::Match(tok->next(), "( ! %var% )|&&")) { varInfo1.erase(tok->tokAt(3)->varId()); } else if (Token::Match(tok->next(), "( %var% ( ! %var% ) )|&&")) { varInfo1.erase(tok->tokAt(5)->varId()); } else if (Token::Match(tok->next(), "( %var% < 0 )|&&")) { varInfo1.erase(tok->tokAt(2)->varId()); } else if (Token::Match(tok->next(), "( 0 > %var% )|&&")) { varInfo1.erase(tok->tokAt(4)->varId()); } else if (Token::Match(tok->next(), "( %var% > 0 )|&&")) { varInfo2.erase(tok->tokAt(2)->varId()); } else if (Token::Match(tok->next(), "( 0 < %var% )|&&")) { varInfo2.erase(tok->tokAt(4)->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); // Conditional allocation in varInfo1 std::map::const_iterator it; 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 == 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 == 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.. else if (Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) { varInfo->clear(); break; } // Function call.. else if (Token::Match(tok, "%type% (") && tok->str() != "return") { const int dealloc = _settings->library.dealloc(tok); functionCall(tok, varInfo, dealloc); tok = tok->next()->link(); // Handle scopes that might be noreturn if (dealloc == 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.leakignore.find(functionName) == _settings->library.leakignore.end() && _settings->library.use.find(functionName) == _settings->library.use.end()) varInfo->possibleUsageAll(functionName); } } continue; } // return else if (tok->str() == "return") { ret(tok, *varInfo); varInfo->clear(); } // goto => weird execution path else if (tok->str() == "goto") { varInfo->clear(); } // continue/break else if (Token::Match(tok, "continue|break ;")) { varInfo->clear(); } // throw // TODO: if the execution leave the function then treat it as return else if (tok->str() == "throw") { varInfo->clear(); } } } void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const int dealloc) { // Ignore function call? const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end()); if (ignore) return; std::map &alloctype = varInfo->alloctype; std::map &possibleUsage = varInfo->possibleUsage; for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) { if ((Token::Match(arg, "%var% [-,)]") && arg->varId() > 0) || (Token::Match(arg, "& %var%") && arg->next()->varId() > 0)) { // goto variable if (arg->str() == "&") arg = arg->next(); // Is variable allocated? const std::map::iterator var = alloctype.find(arg->varId()); if (var != alloctype.end()) { if (dealloc == NOALLOC) { // possible usage possibleUsage[arg->varId()] = tok->str(); if (var->second == DEALLOC && arg->previous()->str() == "&") varInfo->erase(arg->varId()); } else if (var->second == DEALLOC) { CheckOther checkOther(_tokenizer, _settings, _errorLogger); checkOther.doubleFreeError(tok, arg->str()); } else if (var->second != dealloc) { // mismatching allocation and deallocation mismatchError(tok, arg->str()); varInfo->erase(arg->varId()); } else { // deallocation var->second = DEALLOC; } } else if (dealloc != NOALLOC) { alloctype[arg->varId()] = DEALLOC; } } else if (Token::Match(arg, "%var% (")) { functionCall(arg, varInfo, dealloc); } } } 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 != DEALLOC) { const std::map::const_iterator use = possibleUsage.find(vartok->varId()); if (use == possibleUsage.end()) { leakError(vartok, vartok->str(), var->second); } 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 != 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% . %var% [);,]", varid)) { used = true; break; } } // return deallocated pointer if (used && it->second == DEALLOC) deallocReturnError(tok, var->name()); else if (!used && it->second != DEALLOC) { const std::map::const_iterator use = possibleUsage.find(varid); if (use == possibleUsage.end()) { leakError(tok, var->name(), it->second); } else { configurationInfo(tok, use->second); } } } } } cppcheck-1.66/lib/checkleakautovar.h000066400000000000000000000104161236713773000174740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" #include #include class CPPCHECKLIB VarInfo { public: 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); } 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 int dealloc); /** 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); /** 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(0, settings, errorLogger); c.deallocReturnError(0, "p"); c.configurationInfo(0, "f"); // user configuration is needed to complete analysis } static std::string myName() { return "Leaks (auto variables)"; } std::string classInfo() const { return "Detect when a auto variable is allocated but not deallocated.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkleakautovarH cppcheck-1.66/lib/checkmemoryleak.cpp000066400000000000000000003231031236713773000176560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "symboldatabase.h" #include "mathlib.h" #include "tokenize.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; } /** * 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 needs to be alphabetically sorted so we can run bsearch on it. * This list contains function names with const parameters e.g.: atof(const char *) * Reference: http://www.aquaphoenix.com/ref/gnu_c_library/libc_492.html#SEC492 */ static const char * const call_func_white_list[] = { "_open", "_wopen", "access", "adjtime", "asctime", "asctime_r", "asprintf", "assert" , "atof", "atoi", "atol", "chdir", "chmod", "chown" , "clearerr", "creat", "ctime", "ctime_r", "delete", "execl", "execle" , "execlp", "execv", "execve", "fchmod", "fclose", "fcntl" , "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets" , "flock", "fmemopen", "fnmatch", "fopen", "fopencookie", "for", "fprintf", "fputc", "fputs", "fread", "free" , "freopen", "fscanf", "fseek", "fseeko", "fsetpos", "fstat", "fsync", "ftell", "ftello" , "ftruncate", "fwrite", "getc", "getenv","getgrnam", "gethostbyaddr", "gethostbyname", "getnetbyname" , "getopt", "getopt_long", "getprotobyname", "getpwnam", "gets", "getservbyname", "getservbyport" , "glob", "gmtime", "gmtime_r", "if", "index", "inet_addr", "inet_aton", "inet_network", "initgroups", "ioctl" , "link", "localtime", "localtime_r" , "lockf", "lseek", "lstat", "mblen", "mbstowcs", "mbtowc", "memchr", "memcmp", "memcpy", "memmove", "memset" , "mkdir", "mkfifo", "mknod", "mkstemp" , "obstack_printf", "obstack_vprintf", "open", "opendir", "parse_printf_format", "pathconf" , "perror", "popen" ,"posix_fadvise", "posix_fallocate", "pread" , "printf", "psignal", "puts", "pwrite", "qsort", "read", "readahead", "readdir", "readdir_r" , "readlink", "readv" , "realloc", "regcomp", "remove", "rename", "return", "rewind", "rewinddir", "rindex" , "rmdir" ,"scandir", "scanf", "seekdir" , "setbuf", "setbuffer", "sethostname", "setlinebuf", "setlocale" ,"setvbuf", "sizeof" ,"snprintf", "sprintf", "sscanf" , "stat", "stpcpy", "strcasecmp", "strcat", "strchr", "strcmp", "strcoll" , "strcpy", "strcspn", "strdup", "stricmp", "strlen", "strncasecmp", "strncat", "strncmp" , "strncpy", "strpbrk","strrchr", "strspn", "strstr", "strtod", "strtok", "strtol", "strtoul", "strxfrm", "switch" , "symlink", "sync_file_range", "system", "telldir", "tempnam", "time", "typeid", "unlink" , "utime", "utimes", "vasprintf", "vfprintf", "vfscanf", "vprintf" , "vscanf", "vsnprintf", "vsprintf", "vsscanf", "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->isName()) return No; if (!Token::Match(tok2, "%type%|%var% ::|. %type%")) { // Does tok2 point on "malloc", "strdup" or "kmalloc".. static const char * const mallocfunc[] = { "malloc", "calloc", "strdup", "strndup", "kmalloc", "kzalloc", "kcalloc" }; for (unsigned int i = 0; i < sizeof(mallocfunc)/sizeof(*mallocfunc); i++) { if (tok2->str() == mallocfunc[i]) return Malloc; } // Using realloc.. if (varid && Token::Match(tok2, "realloc ( %any% ,") && tok2->tokAt(2)->varId() != varid) return Malloc; if (Token::Match(tok2, "new struct| %type% [;()]") || Token::Match(tok2, "new ( std :: nothrow ) struct| %type% [;()]") || Token::Match(tok2, "new ( nothrow ) struct| %type% [;()]")) return New; if (Token::Match(tok2, "new struct| %type% [") || Token::Match(tok2, "new ( std :: nothrow ) struct| %type% [") || Token::Match(tok2, "new ( nothrow ) struct| %type% [")) return NewArray; if (Token::Match(tok2, "fopen|tmpfile|g_fopen (")) return File; 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 (tokenizer && Token::findmatch(tokenizer->tokens(), ("%type% *|&| " + tok2->str()).c_str())) return No; return Fd; } if (Token::simpleMatch(tok2, "popen (")) return Pipe; } // Does tok2 point on "g_malloc", "g_strdup", .. const int alloctype = settings1->library.alloc(tok2); if (alloctype > 0) { if (alloctype == settings1->library.dealloc("free")) return Malloc; if (alloctype == settings1->library.dealloc("fclose")) return File; return Library::ismemory(alloctype) ? OtherMem : OtherRes; } } while (Token::Match(tok2,"%type%|%var% ::|. %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, "%var% ( %varid% [,)]", varid)) return No; if (tok2->str() == "realloc") return Malloc; // GTK memory reallocation.. //if (Token::Match(tok2, "g_realloc|g_try_realloc|g_renew|g_try_renew")) return No; } CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, unsigned int varid) const { if (Token::Match(tok, "delete %varid% ;", varid)) return New; if (Token::Match(tok, "delete [ ] %varid% ;", varid)) return NewArray; if (Token::Match(tok, "delete ( %varid% ) ;", varid)) return New; if (Token::Match(tok, "delete [ ] ( %varid% ) ;", varid)) return NewArray; if (tok && tok->str() == "::") tok = tok->next(); if (Token::Match(tok, "free|kfree ( %varid% ) [;:]", varid) || Token::Match(tok, "free|kfree ( %varid% -", varid) || Token::Match(tok, "realloc ( %varid% , 0 ) ;", varid)) return Malloc; if (Token::Match(tok, "fclose ( %varid% )", varid) || Token::simpleMatch(tok, "fcloseall ( )")) return File; if (settings1->standards.posix) { if (Token::Match(tok, "close ( %varid% )", varid)) return Fd; if (Token::Match(tok, "pclose ( %varid% )", varid)) return Pipe; } // Does tok2 point on "g_free", etc .. if (Token::Match(tok, "%type% ( %varid% )", varid)) { const int dealloctype = settings1->library.dealloc(tok); if (dealloctype > 0) return Library::ismemory(dealloctype) ? OtherMem : OtherRes; } return No; } CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, const std::string &varname) const { if (Token::Match(tok, std::string("delete " + varname + " [,;]").c_str())) return New; if (Token::Match(tok, std::string("delete [ ] " + varname + " [,;]").c_str())) return NewArray; if (Token::Match(tok, std::string("delete ( " + varname + " ) [,;]").c_str())) return New; if (Token::Match(tok, std::string("delete [ ] ( " + varname + " ) [,;]").c_str())) return NewArray; if (Token::simpleMatch(tok, std::string("free ( " + varname + " ) ;").c_str()) || Token::simpleMatch(tok, std::string("kfree ( " + varname + " ) ;").c_str()) || Token::simpleMatch(tok, std::string("realloc ( " + varname + " , 0 ) ;").c_str())) return Malloc; if (Token::simpleMatch(tok, std::string("fclose ( " + varname + " )").c_str()) || Token::simpleMatch(tok, "fcloseall ( )")) return File; if (Token::simpleMatch(tok, std::string("close ( " + varname + " )").c_str())) return Fd; if (Token::simpleMatch(tok, std::string("pclose ( " + varname + " )").c_str())) return Pipe; if (Token::Match(tok, ("%type% ( " + varname + " )").c_str())) { int type = settings1->library.dealloc(tok); if (type > 0) return Library::ismemory(type) ? OtherMem : OtherRes; } return No; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- void CheckMemoryLeak::memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) { 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 { std::list callstack; if (tok) callstack.push_back(tok); reportErr(callstack, severity, id, msg); } void CheckMemoryLeak::reportErr(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg) const { const ErrorLogger::ErrorMessage errmsg(callstack, tokenizer?&tokenizer->list:0, severity, id, msg, 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); } 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"); } 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); } void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocDealloc", "Deallocating a deallocated pointer: " + varname); } void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocuse", "Dereferencing '" + varname + "' after it is deallocated / released"); } void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz) const { reportErr(tok, Severity::error, "mismatchSize", "The given size " + sz + " is mismatching"); } void CheckMemoryLeak::mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const { reportErr(callstack, Severity::error, "mismatchAllocDealloc", "Mismatching allocation and deallocation: " + varname); } CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list *callstack) const { if (!func || !func->hasBody) return No; // Get return pointer.. unsigned int varid = 0; unsigned int indentlevel = 0; for (const Token *tok2 = func->functionScope->classStart; tok2 != func->functionScope->classEnd; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { if (indentlevel <= 1) return No; --indentlevel; } if (Token::Match(tok2, "return %var% ;")) { if (indentlevel != 1) return No; varid = tok2->next()->varId(); break; } else if (tok2->str() == "return") { AllocType allocType = getAllocationType(tok2->next(), 0, callstack); if (allocType != No) return allocType; } } // 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 (Token::Match(tok, "static %type% * %varid% [;{}=]", varid)) { return No; } if (Token::Match(tok, "[(,] %varid% [,)]", varid)) { return No; } if (tok->str() == "return") return allocType; } return allocType; } const char *CheckMemoryLeak::functionArgAlloc(const Function *func, unsigned int targetpar, AllocType &allocType) const { allocType = No; if (!func || !func->functionScope) 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. int realloc = 0; 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 ( * %var% )")) { realloc = 1; allocType = No; } else if (Token::Match(tok->previous(), "* %var% =")) { 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 ""; } void CheckMemoryLeakInFunction::parse_noreturn() { if (noreturn.empty()) { noreturn.insert("exit"); noreturn.insert("_exit"); noreturn.insert("_Exit"); noreturn.insert("abort"); noreturn.insert("err"); noreturn.insert("verr"); noreturn.insert("errx"); noreturn.insert("verrx"); noreturn.insert("ExitProcess"); noreturn.insert("ExitThread"); noreturn.insert("pthread_exit"); } // only check functions const std::size_t functionsCount = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functionsCount; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; // parse this function to check if it contains an "exit" call.. bool isNoreturn = false; for (const Token *tok2 = scope->classStart->next(); tok2 != scope->classEnd; tok2 = tok2->next()) { if (Token::Match(tok2->previous(), "[;{}] exit (")) { isNoreturn = true; break; } } // This function is not a noreturn function if (isNoreturn) noreturn.insert(scope->className); else notnoreturn.insert(scope->className); } } bool CheckMemoryLeakInFunction::notvar(const Token *tok, unsigned int varid, bool endpar) { const std::string end(endpar ? " &&|)" : " [;)&|]"); return bool(Token::Match(tok, ("! %varid%" + end).c_str(), varid) || Token::Match(tok, ("! ( %varid% )" + end).c_str(), varid)); } bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname) { return (std::binary_search(call_func_white_list, call_func_white_list+sizeof(call_func_white_list) / sizeof(call_func_white_list[0]), funcname)); } bool CheckMemoryLeakInFunction::test_white_list_with_lib(const std::string &funcname, const Settings *settings) { return (std::binary_search(call_func_white_list, call_func_white_list+sizeof(call_func_white_list) / sizeof(call_func_white_list[0]), funcname) || (settings->library.leakignore.find(funcname) != settings->library.leakignore.end())); } 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_with_lib(tok->str(), _settings)) { if (tok->str() == "asprintf" || tok->str() == "delete" || tok->str() == "fclose" || tok->str() == "for" || tok->str() == "free" || tok->str() == "if" || tok->str() == "realloc" || tok->str() == "return" || tok->str() == "switch" || tok->str() == "while" || tok->str() == "sizeof") { return 0; } // is the varid a parameter? for (const Token *tok2 = tok->tokAt(2); 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 0; } if (noreturn.find(tok->str()) != noreturn.end() && tok->strAt(-1) != "=") return "exit"; if (varid > 0 && (getReallocationType(tok, varid) != No || getDeallocationType(tok, varid) != No)) return 0; 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 0; 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? 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 (notnoreturn.find(funcname) != notnoreturn.end()) 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 (varid > 0 && 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"; const Variable* param = function->getArgumentVar(par-1); if (!param || !param->nameToken()) return "use"; if (!function->functionScope) 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 (varid > 0 && Token::Match(tok, "& %varid% [,()]", varid)) { const Function *func = functok->function(); if (func == 0) 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 (varid > 0 && Token::Match(tok, "%varid% . %var% [,)]", varid)) return "use"; } return (eq || _settings->experimental) ? 0 : "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(0); 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; while (Token::Match(tok2->next(), "%var% .")) tok2 = tok2->tokAt(2); if (Token::Match(tok2->next(), "%var% (")) ; 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 0; } alloc = Malloc; tok = tok->next()->link(); } else { alloc = getAllocationType(tok->tokAt(2), varid); } //bool realloc = false; 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, ";"); //TODO: this assignment is redundant, should be fixed //realloc = true; tok = tok->tokAt(2); if (Token::Match(tok, "%var% (")) 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 (alloc == No && alloctype == No) alloctype = CheckMemoryLeak::New; } if (alloc != No) { //if (! realloc) 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, "%var% = %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) { if (Token::Match(tok2, "[=+(,] %varid%", varid)) { if (!rhs && 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(), "[;{})=|] ::| %var%")) { if (Token::Match(tok, "%varid% ?", varid)) tok = tok->tokAt(2); AllocType dealloc = getDeallocationType(tok, varid); if (dealloc != No && tok->str() == "fcloseall" && alloctype != dealloc) //TODO: this assignment is redundant, should be fixed /*dealloc = No*/; 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 (Token::Match(tok, "if ( 0|-1 <=|< %varid% )", varid) || Token::Match(tok, "if ( %varid% >|>= 0|-1 )", varid) || Token::Match(tok, "if ( %varid% != -1 )", varid) || Token::Match(tok, "if ( -1 != %varid% )", varid)) { addtoken(&rettail, tok, "if(var)"); tok = tok->next()->link(); continue; } else if (Token::Match(tok, "if ( %varid% == -1 )", varid) || Token::Match(tok, "if ( -1 == %varid% )", varid) || Token::Match(tok, "if ( %varid% < 0 )", varid) || Token::Match(tok, "if ( 0 > %varid% )", varid)) { addtoken(&rettail, tok, "if(!var)"); tok = tok->next()->link(); continue; } } if (Token::Match(tok, "if ( %varid% )", varid)) { addtoken(&rettail, tok, "if(var)"); // Make sure the "use" will not be added tok = tok->next()->link(); continue; } else if (Token::simpleMatch(tok, "if (") && notvar(tok->tokAt(2), varid, true)) { 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, "%var% (") && !test_white_list_with_lib(tok2->str(), _settings)) { 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()) dep = true; } if (Token::Match(tok, "if ( ! %varid% &&", varid)) { addtoken(&rettail, tok, "if(!var)"); } else if (tok->next() && tok->next()->link() && Token::Match(tok->next()->link()->tokAt(-3), "&& ! %varid%", 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 ( true )") || 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->tokAt(2), varid, true)) { addtoken(&rettail, tok, "while(!var)"); tok = end; continue; } addtoken(&rettail, tok, "loop"); if (varid > 0) { for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != end; tok2 = tok2->next()) { if (notvar(tok2, varid)) { addtoken(&rettail, tok2, "!var"); break; } } } 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 f; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { tok = tok2; break; } if (tok2->str() == "(") f.push(tok2->previous()); else if (!f.empty() && tok2->str() == ")") f.pop(); if (tok2->varId() == varid) { // Read data.. if (!Token::Match(tok2->previous(), "&|(") && tok2->strAt(1) == "[") { } else if (f.empty() || !test_white_list_with_lib(f.top()->str(), _settings) || getDeallocationType(f.top(),varid)) { use = true; } } } if (use) addtoken(&rettail, tok, "use"); addtoken(&rettail, tok, ";"); } } // throw.. else if (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, "& %var% = %varid% ;", varid)) { while (rethead->next()) rethead->deleteNext(); return rethead; } if (Token::Match(tok, "[)=] %varid% [+;)]", varid) || (Token::Match(tok, "%var% + %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, "[;{}] %var% [ %varid% ]", varid)) { addtoken(&rettail, tok, "use"); } else if (Token::Match(tok->previous(), ";|{|}|=|(|,|%cop% %varid% [", varid) || 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, "%var% (")) { // 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 (noreturn.find(tok->str()) != noreturn.end()) addtoken(&rettail, tok, "exit"); else if (!test_white_list_with_lib(tok->str(), _settings)) { 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, "%var% ( 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, "( *| %var%") && Token::simpleMatch(tok->link(),") (")) { const Token *tok2 = tok->next(); if (tok2->str() == "*") tok2 = tok2->next(); tok2 = tok2->next(); while (Token::Match(tok2, ". %var%")) 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)) { 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 { { // 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"); } } // 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(), "{ %var% ; }")) { tok2->deleteNext(); tok2->tokAt(2)->deleteNext(); done = false; } if (Token::Match(tok2->next(), "{ %var% %var% ; }")) { 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 %var% ; else %var% ;") && 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 %var% ;" => "if %var% ;" else if (Token::Match(tok2->next(), "if ; else %var% ;")) { 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 (! _settings->experimental && 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; } // 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 ; %var%" => "[;{}] return use ;" if (Token::Match(tok2, "[;{}] return use ; %var%")) { 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 |= (_tok->str() == "case"); 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 && _settings->experimental) { 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 ((result = Token::findsimplematch(tokens, "loop alloc ;")) != nullptr) { return result; } if (Token::Match(tokens, "alloc ; if|if(var)|ifv break|continue|return ;")) { return tokens->tokAt(3); } 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::findsimplematch(tokens, "; alloc ; if assign ;")) != nullptr) { return result->tokAt(4); } if (((result = Token::findsimplematch(tokens, "; alloc ; if dealloc ; }")) != nullptr) && !result->tokAt(7)) { 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 *Tok1, 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(Tok1, 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") == 0) { 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") == 0) { 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 (_settings->debugwarnings) { Token *first = tok; while (first && first->str() == ";") first = first->next(); bool noerr = false; noerr |= Token::simpleMatch(first, "alloc ; }"); noerr |= Token::simpleMatch(first, "alloc ; dealloc ; }"); noerr |= Token::simpleMatch(first, "alloc ; return use ; }"); noerr |= Token::simpleMatch(first, "alloc ; use ; }"); noerr |= Token::simpleMatch(first, "alloc ; use ; return ; }"); noerr |= Token::simpleMatch(first, "alloc ; dealloc ; return ; }"); noerr |= Token::simpleMatch(first, "if alloc ; dealloc ; }"); noerr |= Token::simpleMatch(first, "if alloc ; return use ; }"); noerr |= Token::simpleMatch(first, "if alloc ; use ; }"); noerr |= Token::simpleMatch(first, "alloc ; ifv return ; dealloc ; }"); noerr |= Token::simpleMatch(first, "alloc ; if return ; dealloc; }"); // Unhandled case.. if (!noerr && tok) { std::ostringstream errmsg; errmsg << "inconclusive leak of " << varname << ": "; errmsg << tok->stringifyList(false, false, false, false, false, 0, 0); reportError(first, Severity::debug, "debug", errmsg.str()); } } 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, "%var% = realloc|g_try_realloc ( %var% , %any%") && 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, "%var% = %varid% ;", tok, tok->varId()) || Token::findmatch(scope->classStart, "[{};] %varid% = %var% [;=]", 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::Match(tokEndRealloc->next(), "; if ( ! %varid% ) {", tok->varId())) { const Token* tokEndBrace = tokEndRealloc->linkAt(7); if (tokEndBrace && Token::simpleMatch(tokEndBrace->tokAt(-2), ") ;") && Token::Match(tokEndBrace->linkAt(-2)->tokAt(-2), "{|}|; %var% (")) continue; } memleakUponReallocFailureError(tok, tok->str()); } else if (tok->next()->varId() > 0 && (Token::Match(tok, "* %var% = realloc|g_try_realloc ( * %var% , %any%") && 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, "%var% = * %varid% ;", tok, tok->next()->varId()) || Token::findmatch(scope->classStart, "[{};] * %varid% = %var% [;=]", 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), "{|}|; %var% (")) 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 != 0); } void CheckMemoryLeakInFunction::check() { // fill the "noreturn" parse_noreturn(); // 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]; checkScope(scope->classStart->next(), "", 0, scope->functionOf != nullptr, 1); } // Check variables.. for (unsigned int i = 0; 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; 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(); if (tok->isStandardType()) { if (var->isPrivate()) checkPublicFunctions(&(*scope), var->nameToken()); variable(&(*scope), var->nameToken()); } // known class? else if (var->type()) { // not derived? if (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; std::list callstack; if (alloc != CheckMemoryLeak::Many && Dealloc != CheckMemoryLeak::No && Dealloc != CheckMemoryLeak::Many && Dealloc != alloc) { callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } Alloc = alloc; } } if (!body) continue; // Deallocate.. AllocType dealloc = getDeallocationType(tok, varname); if (dealloc == No) { std::string temp = scope->className + " :: " + varname; dealloc = getDeallocationType(tok, temp); } if (dealloc == No) { std::string temp = "this . " + varname; dealloc = getDeallocationType(tok, temp); } // 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; std::list callstack; if (dealloc != CheckMemoryLeak::Many && Alloc != CheckMemoryLeak::No && Alloc != Many && Alloc != dealloc) { callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } Dealloc = dealloc; } // Function call .. possible deallocation else if (Token::Match(tok->previous(), "[{};] %var% (")) { if (!CheckMemoryLeakInFunction::test_white_list_with_lib(tok->str(), _settings)) { 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) { 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."); } 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("warning")) return; const unsigned int varid = classtok->varId(); if (varid == 0) { _tokenizer->getSymbolDatabase()->debugMessage(classtok, "CheckMemoryInClass::checkPublicFunctions found variable \'" + classtok->str() + "\' with varid 0"); return; } // 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->token; while (tok2->str() != "{") tok2 = tok2->next(); if (Token::Match(tok2, "{|}|; %varid% =", varid)) { const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(3), varid); if (alloc != CheckMemoryLeak::No) publicAllocationError(tok2, tok2->strAt(1)); } else if (Token::Match(tok2, "{|}|; %type% :: %varid% =", varid) && tok2->next()->str() == scope->className) { const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(5), varid); if (alloc != CheckMemoryLeak::No) publicAllocationError(tok2, tok2->strAt(3)); } } } } 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."); } void CheckMemoryLeakStructMember::check() { const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); for (unsigned int i = 0; 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) { // This should be in the CheckMemoryLeak base class static std::set ignoredFunctions; if (ignoredFunctions.empty()) { ignoredFunctions.insert("if"); ignoredFunctions.insert("for"); ignoredFunctions.insert("while"); ignoredFunctions.insert("malloc"); } // Is struct variable a pointer? if (variable->isPointer()) { // Check that variable is allocated with malloc if (!isMalloc(variable)) return; } else if (!_tokenizer->isC()) { // For non-C code a destructor might cleanup members return; } // Check struct.. unsigned int indentlevel2 = 0; for (const Token *tok2 = variable->nameToken(); 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% = malloc|strdup|kmalloc (", variable->declarationId()) || Token::Match(tok2->previous(), "[;{}] %varid% . %var% = new ", variable->declarationId()) || Token::Match(tok2->previous(), "[;{}] %varid% . %var% = fopen (", variable->declarationId())) { 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 (Token::Match(tok3, "free|kfree ( %var% . %varid% )", structmemberid) || Token::Match(tok3, "delete %var% . %varid%", structmemberid) || Token::Match(tok3, "delete [ ] %var% . %varid%", structmemberid) || Token::Match(tok3, "fclose ( %var% . %varid%", structmemberid)) { // 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 (indentlevel2 == 0 && Token::Match(tok3, "free|kfree ( %varid% )", structid)) { memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); break; } // failed allocation => skip code.. else if (Token::Match(tok3, "if ( ! %var% . %varid% )", 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 (Token::Match(tok3, "if ( %var% . %varid% ) {", structmemberid)) { // 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)) { 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, "%var% (")) { // Calling non-function / function that doesn't deallocate? if (ignoredFunctions.find(tok3->str()) != ignoredFunctions.end()) 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% . %var% [,)]", structid)) { /** @todo check if the function deallocates the memory */ deallocated = true; break; } } if (deallocated) break; } } } } } #include "checkuninitvar.h" // CheckUninitVar::analyse void CheckMemoryLeakNoVar::check() { std::set uvarFunctions; { const CheckUninitVar c(_tokenizer, _settings, _errorLogger); c.analyse(_tokenizer->tokens(), uvarFunctions); } 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); // goto the "}" that ends the executable scope.. const Token *tok = scope->classEnd; // parse the executable scope until tok is reached... for (const Token *tok2 = tok->link(); tok2 && tok2 != tok; tok2 = tok2->next()) { // allocating memory in parameter for function call.. if (Token::Match(tok2, "[(,] %var% (") && Token::Match(tok2->linkAt(2), ") [,)]")) { const AllocType allocType = getAllocationType(tok2->next(), 0); if (allocType != No) { // locate outer function call.. for (const Token *tok3 = tok2; tok3; tok3 = tok3->previous()) { if (tok3->str() == "(") { // Is it a function call.. if (!Token::Match(tok3->tokAt(-2), "= %var% (")) { const std::string& functionName = tok3->strAt(-1); if (functionName == "delete" || functionName == "free" || functionName == "fclose" || functionName == "realloc") break; if (CheckMemoryLeakInFunction::test_white_list_with_lib(functionName, _settings)) { functionCallLeak(tok2, tok2->strAt(1), functionName); break; } if (uvarFunctions.find(functionName) != uvarFunctions.end()) { functionCallLeak(tok2, tok2->strAt(1), functionName); break; } } break; } else if (tok3->str() == ")") tok3 = tok3->link(); else if (Token::Match(tok3, "[;{}]")) 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, "{|}|; %var% (") && tok->strAt(-1) != "=") { tok = tok->next(); const int allocationId = _settings->library.alloc(tok); if (allocationId > 0) returnValueNotUsedError(tok, tok->str()); } } } 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."); } void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::string &alloc) { reportError(tok, Severity::error, "leakReturnValNotUsed", "Return value of allocation function " + alloc + " is not used."); } cppcheck-1.66/lib/checkmemoryleak.h000066400000000000000000000426721236713773000173340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" #include #include class Scope; class Function; class Variable; /// @addtogroup Core /// @{ /** @brief Base class for memory leaks checking */ class CPPCHECKLIB CheckMemoryLeak { private: /** For access to the tokens */ const Tokenizer * const tokenizer; /** 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 location the token where the error occurs * @param severity the severity of the bug * @param id type of message * @param msg text */ void reportErr(const Token *location, Severity::SeverityType severity, const std::string &id, const std::string &msg) 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 */ void reportErr(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg) 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); /** * @brief Get type of deallocation at given position * @param tok position * @param varname variable name * @return type of deallocation */ AllocType getDeallocationType(const Token *tok, const std::string &varname) 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 = NULL) 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 = NULL) 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(0, 0, 0), symbolDatabase(NULL) { } /** @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 = 0; } /** @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); static bool test_white_list_with_lib(const std::string &funcname, const Settings *settings); /** @brief Perform checking */ void check(); /** * Checking for a memory leak caused by improper realloc usage. */ void checkReallocUsage(); /** * @brief %Check if there is a "!var" match inside a condition * @param tok first token to match * @param varid variable id * @param endpar if this is true the "!var" must be followed by ")" * @return true if match */ static bool notvar(const Token *tok, unsigned int varid, bool endpar = false); /** * 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 Tok1 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 *Tok1, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz); /** parse tokens to see what functions are "noreturn" */ void parse_noreturn(); private: /** Report all possible errors (for the --errorlist) */ void getErrorMessages(ErrorLogger *e, const Settings *settings) const { CheckMemoryLeakInFunction c(0, settings, e); c.memleakError(0, "varname"); c.resourceLeakError(0, "varname"); c.deallocDeallocError(0, "varname"); c.deallocuseError(0, "varname"); c.mismatchSizeError(0, "sz"); std::list callstack; c.mismatchAllocDealloc(callstack, "varname"); c.memleakUponReallocFailureError(0, "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"; } /** Function names for functions that are "noreturn" */ std::set noreturn; /** Function names for functions that are not "noreturn" */ std::set notnoreturn; 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(0, 0, 0) { } 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(0, settings, e); c.publicAllocationError(0, "varname"); c.unsafeClassError(0, "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(0, 0, 0) { } 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(0, 0, 0) { } 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); void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall); void returnValueNotUsedError(const Token* tok, const std::string &alloc); void getErrorMessages(ErrorLogger *e, const Settings *settings) const { CheckMemoryLeakNoVar c(0, settings, e); c.functionCallLeak(0, "funcName", "funcName"); c.returnValueNotUsedError(0, "funcName"); } 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.66/lib/checknonreentrantfunctions.cpp000066400000000000000000000050701236713773000221570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Find non reentrant functions //--------------------------------------------------------------------------- #include "checknonreentrantfunctions.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckNonReentrantFunctions instance; } void CheckNonReentrantFunctions::nonReentrantFunctions() { if (!_settings->standards.posix || !_settings->isEnabled("portability")) return; std::map::const_iterator nonReentrant_end = _nonReentrantFunctions.end(); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Look for function invocations if (!tok->isName() || tok->strAt(1) != "(" || tok->varId() != 0) continue; // Check for non-reentrant function name std::map::const_iterator it = _nonReentrantFunctions.find(tok->str()); if (it == nonReentrant_end) continue; const Token *prev = tok->previous(); if (prev) { // Ignore function definitions, class members or class definitions if (prev->isName() || Token::Match(prev, ".|:")) continue; // Check for "std" or global namespace, ignore other namespaces if (prev->str() == "::" && prev->previous() && prev->previous()->str() != "std" && prev->previous()->isName()) continue; } // Only affecting multi threaded code, therefore this is "portability" reportError(tok, Severity::portability, "nonreentrantFunctions"+it->first, it->second); } } //--------------------------------------------------------------------------- cppcheck-1.66/lib/checknonreentrantfunctions.h000066400000000000000000000105761236713773000216330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checknonreentrantfunctionsH #define checknonreentrantfunctionsH //--------------------------------------------------------------------------- #include "config.h" #include "check.h" #include #include /// @addtogroup Checks /// @{ /** * @brief Using non reentrant functions that can be replaced by their reentrant versions */ class CPPCHECKLIB CheckNonReentrantFunctions : public Check { public: /** This constructor is used when registering the CheckNonReentrantFunctions */ CheckNonReentrantFunctions() : Check(myName()) { initNonReentrantFunctions(); } /** This constructor is used when running checks. */ CheckNonReentrantFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { initNonReentrantFunctions(); } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckNonReentrantFunctions checkNonReentrantFunctions(tokenizer, settings, errorLogger); checkNonReentrantFunctions.nonReentrantFunctions(); } /** Check for non reentrant functions */ void nonReentrantFunctions(); private: /* function name / error message */ std::map _nonReentrantFunctions; /** init nonreentrant functions list ' */ void initNonReentrantFunctions() { static const char * const non_reentrant_functions_list[] = { "localtime", "gmtime", "strtok", "gethostbyname", "gethostbyaddr", "getservbyname" , "getservbyport", "crypt", "ttyname", "gethostbyname2" , "getprotobyname", "getnetbyname", "getnetbyaddr", "getrpcbyname", "getrpcbynumber", "getrpcent" , "ctermid", "readdir", "getlogin", "getpwent", "getpwnam", "getpwuid", "getspent" , "fgetspent", "getspnam", "getgrnam", "getgrgid", "getnetgrent", "tempnam", "fgetpwent" , "fgetgrent", "ecvt", "gcvt", "getservent", "gethostent", "getgrent", "fcvt" }; // generate messages for (unsigned int i = 0; i < (sizeof(non_reentrant_functions_list) / sizeof(char *)); ++i) { std::string strMsg("Non reentrant function '"); strMsg+=non_reentrant_functions_list[i]; strMsg+= "' called. For threadsafe applications it is recommended to use the reentrant replacement function '"; strMsg+=non_reentrant_functions_list[i]; strMsg+="_r'."; _nonReentrantFunctions[non_reentrant_functions_list[i]] = strMsg; } } void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckNonReentrantFunctions c(0, settings, errorLogger); std::map::const_iterator it(_nonReentrantFunctions.begin()), itend(_nonReentrantFunctions.end()); for (; it!=itend; ++it) { c.reportError(0, Severity::portability, "nonreentrantFunctions"+it->first, it->second); } } static std::string myName() { return "Non reentrant functions"; } std::string classInfo() const { std::string info = "Warn if any of these non reentrant functions are used:\n"; std::map::const_iterator it(_nonReentrantFunctions.begin()), itend(_nonReentrantFunctions.end()); for (; it!=itend; ++it) { info += "* " + it->first + "\n"; } return info; } }; /// @} //--------------------------------------------------------------------------- #endif // checknonreentrantfunctionsH cppcheck-1.66/lib/checknullpointer.cpp000066400000000000000000000666511236713773000201000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mathlib.h" #include "symboldatabase.h" #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckNullPointer instance; } //--------------------------------------------------------------------------- /** * @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 * @param value 0 => invalid with null pointers as parameter. * 1-.. => only invalid with uninitialized data. */ void CheckNullPointer::parseFunctionCall(const Token &tok, std::list &var, const Library *library, unsigned char value) { if (Token::Match(&tok, "%var% ( )") || !tok.tokAt(2)) return; const Token* firstParam = tok.tokAt(2); const Token* secondParam = firstParam->nextArgument(); // 1st parameter.. if ((Token::Match(firstParam, "%var% ,|)") && firstParam->varId() > 0) || (value == 0 && Token::Match(firstParam, "0|NULL ,|)"))) { if (value == 0 && Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && secondParam && secondParam->str() != "0") // Only if length (second parameter) is not zero var.push_back(firstParam); else if (value == 0 && library != nullptr && library->isnullargbad(tok.str(),1)) var.push_back(firstParam); else if (value == 1 && library != nullptr && library->isuninitargbad(tok.str(),1)) var.push_back(firstParam); } // 2nd parameter.. if ((value == 0 && Token::Match(secondParam, "0|NULL ,|)")) || (secondParam && secondParam->varId() > 0 && Token::Match(secondParam->next(),"[,)]"))) { if (value == 0 && library != nullptr && library->isnullargbad(tok.str(),2)) var.push_back(secondParam); else if (value == 1 && library != nullptr && library->isuninitargbad(tok.str(),2)) var.push_back(secondParam); } if (Token::Match(&tok, "printf|sprintf|snprintf|fprintf|fnprintf|scanf|sscanf|fscanf|wprintf|swprintf|fwprintf|wscanf|swscanf|fwscanf")) { const Token* argListTok = 0; // 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->type() == 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->type() == 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) && (!scan || value == 0)) { if ((value == 0 && argListTok->str() == "0") || (argListTok->varId() > 0 && Token::Match(argListTok,"%var% [,)]"))) { 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; } } } } } /** * 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) { // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_stream [] = { "fstream", "ifstream", "iostream", "istream", "istringstream", "ofstream", "ostream", "ostringstream", "stringstream", "wistringstream", "wostringstream", "wstringstream" }; unknown = false; const Token* parent = tok->astParent(); if (!parent) return false; 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 (parent->str() == "[" && (!parent->astParent() || parent->astParent()->str() != "&")) return true; // 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, "%var% (")) return true; if (Token::Match(tok, "%var% = %var% .") && tok->varId() > 0 && 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(), "%var% (") && tok->strAt(1) == ")") { const Variable* var = tok->tokAt(-2)->variable(); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string|wstring !!::")) 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() && Token::Match(ovar->typeStartToken(), "std :: string|wstring !!::")) return true; // assume that it's not a dereference (no false positives) return false; } // check if function can assign pointer bool CheckNullPointer::CanFunctionAssignPointer(const Token *functiontoken, unsigned int varid, bool& unknown) { if (Token::Match(functiontoken, "if|while|for|switch|sizeof|catch")) return false; unsigned int argumentNumber = 0; for (const Token *arg = functiontoken->tokAt(2); arg; arg = arg->nextArgument()) { if (Token::Match(arg, "%varid% [,)]", varid)) { const Function* func = functiontoken->function(); if (!func) { // Unknown function unknown = true; return true; // assume that the function might assign the pointer } const Variable* var = func->getArgumentVar(argumentNumber); if (!var) { // Unknown variable unknown = true; return true; } else if (var->isReference()) // Assume every pointer passed by reference is assigned return true; else return false; } ++argumentNumber; } // pointer is not passed return false; } void CheckNullPointer::nullPointerLinkedList() { 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% . %var%")) { // 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()); // We don't support variables without a varid if (varid == 0) continue; if (Token::Match(tok2->tokAt(-2), "%varid% ?", varid)) continue; // Check usage of dereferenced variable in the loop.. for (std::list::const_iterator j = i->nestedList.begin(); j != i->nestedList.end(); ++j) { Scope* 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) { nullPointerError(tok1, var->name(), scope->classDef); 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() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 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 (!_settings->inconclusive && value->inconclusive) continue; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %var% [,)]")) { 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, 0); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { if (value->condition == nullptr) nullPointerError(tok, tok->str()); else if (_settings->isEnabled("warning")) nullPointerError(tok, tok->str(), value->condition, value->inconclusive); } continue; } // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (_settings->inconclusive && unknown) { if (value->condition == nullptr) nullPointerError(tok, tok->str(), true); else nullPointerError(tok, tok->str(), value->condition, true); } continue; } if (value->condition == nullptr) nullPointerError(tok, tok->str(), value->inconclusive); else if (_settings->isEnabled("warning")) nullPointerError(tok, tok->str(), value->condition, value->inconclusive); } } void CheckNullPointer::nullPointer() { nullPointerLinkedList(); if (_settings->isEnabled("warning")) { nullPointerByDeRefAndChec(); nullPointerDefaultArgument(); } } /** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_stream[] = { "fstream", "ifstream", "iostream", "istream", "istringstream", "stringstream", "wistringstream", "wstringstream" }; 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 == 0 || !scope->function->hasBody) // We only look for functions with a body continue; const Token *tok = scope->classStart; if (scope->function && 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(), "!!. %var% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::simpleMatch(tok->tokAt(2), "0 )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string|wstring !!::")) nullPointerError(tok); } else { // function call std::list var; parseFunctionCall(*tok, var, &_settings->library, 0); // 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 [,)]")) { nullPointerError(*it); } } } } else if (Token::Match(tok, "std :: string|wstring ( 0 )")) nullPointerError(tok); else if (Token::simpleMatch(tok->previous(), ">> 0")) { // 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_stream)) nullPointerError(tok); } } const Variable *ovar = nullptr; if (Token::Match(tok, "0 ==|!= %var% !!.")) ovar = tok->tokAt(2)->variable(); else if (Token::Match(tok, "%var% ==|!= 0")) ovar = tok->variable(); else if (Token::Match(tok, "%var% =|+ 0 )|]|,|;|+")) ovar = tok->variable(); if (ovar && !ovar->isPointer() && !ovar->isArray() && Token::Match(ovar->typeStartToken(), "std :: string|wstring !!::")) nullPointerError(tok); } } } /** * @brief If tok is a function call that passes in a pointer such that * the pointer may be modified, this function will remove that * pointer from pointerArgs. */ void CheckNullPointer::removeAssignedVarFromSet(const Token* tok, std::set& pointerArgs) { // If a pointer's address is passed into a function, stop considering it if (Token::Match(tok->previous(), "[;{}] %var% (")) { // Common functions that are known NOT to modify their pointer argument const char safeFunctions[] = "printf|sprintf|fprintf|vprintf"; const Token* endParen = tok->next()->link(); for (const Token* tok2 = tok->next(); tok2 != endParen; tok2 = tok2->next()) { if (tok2->isName() && tok2->varId() > 0 && !Token::Match(tok, safeFunctions)) { pointerArgs.erase(tok2->varId()); } } } } /** * @brief Does one part of the check for nullPointer(). * -# default argument that sets a pointer to 0 * -# dereference pointer */ void CheckNullPointer::nullPointerDefaultArgument() { 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 == 0 || !scope->function->hasBody) // We only look for functions with a body continue; // Scan the argument list for default arguments that are pointers and // which default to a NULL pointer if no argument is specified. std::set pointerArgs; for (const Token *tok = scope->function->arg; tok != scope->function->arg->link(); tok = tok->next()) { if (Token::Match(tok, "%var% = 0 ,|)") && tok->varId() != 0) { const Variable *var = tok->variable(); if (var && var->isPointer()) pointerArgs.insert(tok->varId()); } } // Report an error if any of the default-NULL arguments are dereferenced if (!pointerArgs.empty()) { for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { // If we encounter a possible NULL-pointer check, skip over its body if (tok->str() == "?") { // TODO: Skip this if the condition is unrelated to the variables // Find end of statement tok = tok->astOperand2(); while (tok && !Token::Match(tok, ")|;")) { if (tok->link() && Token::Match(tok, "(|[|<|{")) tok = tok->link(); tok = tok->next(); } if (!tok) break; } else if (Token::simpleMatch(tok, "if ( ")) { bool dependsOnPointer = false; const Token *endOfCondition = tok->next()->link(); if (!endOfCondition) continue; const Token *startOfIfBlock = Token::simpleMatch(endOfCondition, ") {") ? endOfCondition->next() : nullptr; if (!startOfIfBlock) continue; // If this if() statement may return, it may be a null // pointer check for the pointers referenced in its condition const Token *endOfIf = startOfIfBlock->link(); bool isExitOrReturn = Token::findmatch(startOfIfBlock, "exit|return|throw", endOfIf) != nullptr; if (Token::Match(tok, "if ( %var% == 0 )")) { const unsigned int var = tok->tokAt(2)->varId(); if (var > 0 && pointerArgs.count(var) > 0) { if (isExitOrReturn) pointerArgs.erase(var); else dependsOnPointer = true; } } else { for (const Token *tok2 = tok->next(); tok2 != endOfCondition; tok2 = tok2->next()) { if (tok2->isName() && tok2->varId() > 0 && pointerArgs.count(tok2->varId()) > 0) { // If the if() depends on a pointer and may return, stop // considering that pointer because it may be a NULL-pointer // check that returns if the pointer is NULL. if (isExitOrReturn) pointerArgs.erase(tok2->varId()); else dependsOnPointer = true; } } } if (dependsOnPointer && endOfIf) { for (; tok != endOfIf; tok = tok->next()) { // If a pointer is assigned a new value, stop considering it. if (Token::Match(tok, "%var% =")) pointerArgs.erase(tok->varId()); else removeAssignedVarFromSet(tok, pointerArgs); } continue; } } // If there is a noreturn function (e.g. exit()), stop considering the rest of // this function. bool unknown = false; if (Token::Match(tok, "return|throw|exit") || (_tokenizer->IsScopeNoReturn(tok, &unknown) && !unknown)) break; removeAssignedVarFromSet(tok, pointerArgs); if (tok->varId() == 0 || pointerArgs.count(tok->varId()) == 0) continue; // If a pointer is assigned a new value, stop considering it. if (Token::Match(tok, "%var% =")) pointerArgs.erase(tok->varId()); // If a pointer dereference is preceded by an && or ||, // they serve as a sequence point so the dereference // may not be executed. if (isPointerDeRef(tok, unknown) && !unknown && tok->strAt(-1) != "&&" && tok->strAt(-1) != "||" && tok->strAt(-2) != "&&" && tok->strAt(-2) != "||") nullPointerDefaultArgError(tok, tok->str()); } } } } void CheckNullPointer::nullPointerError(const Token *tok) { reportError(tok, Severity::error, "nullPointer", "Null pointer dereference"); } void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, bool inconclusive) { reportError(tok, Severity::error, "nullPointer", "Possible null pointer dereference: " + varname, inconclusive); } void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const Token* nullCheck, bool inconclusive) { std::list callstack; callstack.push_back(tok); callstack.push_back(nullCheck); const std::string errmsg("Possible null pointer dereference: " + varname + " - otherwise it is redundant to check it against null."); reportError(callstack, Severity::warning, "nullPointer", errmsg, inconclusive); } void CheckNullPointer::nullPointerDefaultArgError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "nullPointer", "Possible null pointer dereference if the default parameter value is used: " + varname); } cppcheck-1.66/lib/checknullpointer.h000066400000000000000000000130761236713773000175360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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(); } /** @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 * @param value 0 => invalid with null pointers as parameter. * non-zero => invalid with uninitialized data. */ static void parseFunctionCall(const Token &tok, std::list &var, const Library *library, unsigned char value); /** * 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); // variable name unknown / doesn't exist void nullPointerError(const Token *tok, const std::string &varname, bool inconclusive=false); void nullPointerError(const Token *tok, const std::string &varname, const Token* nullcheck, bool inconclusive = false); void nullPointerDefaultArgError(const Token *tok, const std::string &varname); private: /** Get error messages. Used by --errorlist */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckNullPointer c(0, settings, errorLogger); c.nullPointerError(0, "pointer"); } /** 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"; } /** * @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(); /** * @brief Does one part of the check for nullPointer(). * -# initialize pointer to 0 * -# conditionally assign pointer * -# dereference pointer */ void nullPointerConditionalAssignment(); /** * @brief Does one part of the check for nullPointer(). * -# default argument that sets a pointer to 0 * -# dereference pointer */ void nullPointerDefaultArgument(); /** * @brief Removes any variable that may be assigned from pointerArgs. */ static void removeAssignedVarFromSet(const Token* tok, std::set& pointerArgs); /** * @brief Investigate if function call can make pointer null. If * the pointer is passed by value it can't be made a null pointer. */ static bool CanFunctionAssignPointer(const Token *functiontoken, unsigned int varid, bool& unknown); }; /// @} //--------------------------------------------------------------------------- #endif // checknullpointerH cppcheck-1.66/lib/checkobsoletefunctions.cpp000066400000000000000000000071661236713773000212660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Obsolete functions //--------------------------------------------------------------------------- #include "checkobsoletefunctions.h" #include "symboldatabase.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckObsoleteFunctions instance; } void CheckObsoleteFunctions::obsoleteFunctions() { if (!_settings->isEnabled("style")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Functions defined somewhere? for (unsigned int i = 0; i < symbolDatabase->functionScopes.size(); i++) { const Scope* scope = symbolDatabase->functionScopes[i]; _obsoleteStandardFunctions.erase(scope->className); _obsoletePosixFunctions.erase(scope->className); _obsoleteC99Functions.erase(scope->className); } 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 (tok->isName() && tok->varId()==0 && (tok->next() && tok->next()->str() == "(") && (!Token::Match(tok->previous(), ".|::") || Token::simpleMatch(tok->tokAt(-2), "std ::"))) { std::map::const_iterator it = _obsoleteStandardFunctions.find(tok->str()); if (it != _obsoleteStandardFunctions.end()) { // If checking an old code base it might be uninteresting to update obsolete functions. reportError(tok, Severity::style, "obsoleteFunctions"+it->first, it->second); } else { if (_settings->standards.posix) { it = _obsoletePosixFunctions.find(tok->str()); if (it != _obsoletePosixFunctions.end()) { // If checking an old code base it might be uninteresting to update obsolete functions. reportError(tok, Severity::style, "obsoleteFunctions"+it->first, it->second); } } if (_settings->standards.c >= Standards::C99) { // alloca : this function is obsolete in C but not in C++ (#4382) it = _obsoleteC99Functions.find(tok->str()); if (it != _obsoleteC99Functions.end() && !(tok->str() == "alloca" && _tokenizer->isCPP())) { reportError(tok, Severity::style, "obsoleteFunctions"+it->first, it->second); } } } } } } } //--------------------------------------------------------------------------- cppcheck-1.66/lib/checkobsoletefunctions.h000066400000000000000000000173171236713773000207320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkobsoletefunctionsH #define checkobsoletefunctionsH //--------------------------------------------------------------------------- #include "config.h" #include "check.h" #include #include /// @addtogroup Checks /// @{ /** * @brief Using obsolete functions that are always insecure to use. */ class CPPCHECKLIB CheckObsoleteFunctions : public Check { public: /** This constructor is used when registering the CheckObsoleteFunctions */ CheckObsoleteFunctions() : Check(myName()) { initObsoleteFunctions(); } /** This constructor is used when running checks. */ CheckObsoleteFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { initObsoleteFunctions(); } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckObsoleteFunctions checkObsoleteFunctions(tokenizer, settings, errorLogger); checkObsoleteFunctions.obsoleteFunctions(); } /** Check for obsolete functions */ void obsoleteFunctions(); private: /* function name / error message */ std::map _obsoleteStandardFunctions; std::map _obsoletePosixFunctions; std::map _obsoleteC99Functions; /** init obsolete functions list ' */ void initObsoleteFunctions() { // Obsolete posix functions, which messages suggest only one alternative and doesn't contain additional information. const struct { const char* bad; const char* good; } posix_stdmsgs[] = { {"bsd_signal", "sigaction"}, {"gethostbyaddr", "getnameinfo"}, {"gethostbyname", "getaddrinfo"}, {"bcmp", "memcmp"}, {"bzero", "memset"}, {"ecvt", "sprintf"}, {"fcvt", "sprintf"}, {"gcvt", "sprintf"}, {"getwd", "getcwd"}, {"index", "strchr"}, // See #2334 (using the Qt Model/View function 'index') {"rindex", "strrchr"}, {"pthread_attr_getstackaddr", "pthread_attr_getstack"}, {"pthread_attr_setstackaddr", "pthread_attr_setstack"}, {"vfork", "fork"}, {"wcswcs", "wcsstr"}, {"rand_r", "rand"}, {"utime", "utimensat"}, {"asctime_r", "strftime"}, {"ctime_r", "strftime"} }; for (std::size_t i = 0; i < (sizeof(posix_stdmsgs) / sizeof(*posix_stdmsgs)); ++i) { _obsoletePosixFunctions[posix_stdmsgs[i].bad] = "Obsolete function '" + std::string(posix_stdmsgs[i].bad) + "' called. It is recommended to use the function '" + posix_stdmsgs[i].good + "' instead."; } _obsoletePosixFunctions["usleep"] = "Obsolete function 'usleep' called. It is recommended to use the 'nanosleep' or 'setitimer' function instead.\n" "The obsolete function 'usleep' is called. POSIX.1-2001 declares usleep() function obsolete and POSIX.1-2008 removes it. It is recommended that new applications use the 'nanosleep' or 'setitimer' function."; _obsoletePosixFunctions["bcopy"] = "Obsolete function 'bcopy' called. It is recommended to use the 'memmove' or 'memcpy' function instead."; _obsoletePosixFunctions["ftime"] = "Obsolete function 'ftime' called. It is recommended to use time(), gettimeofday() or clock_gettime() instead."; _obsoletePosixFunctions["getcontext"] = "Obsolete function 'getcontext' called. Due to portability issues, applications are recommended to be rewritten to use POSIX threads."; _obsoletePosixFunctions["makecontext"] = "Obsolete function 'makecontext' called. Due to portability issues, applications are recommended to be rewritten to use POSIX threads."; _obsoletePosixFunctions["swapcontext"] = "Obsolete function 'swapcontext' called. Due to portability issues, applications are recommended to be rewritten to use POSIX threads."; _obsoletePosixFunctions["scalbln"] = "Obsolete function 'scalb' called. It is recommended to use 'scalbln', 'scalblnf' or 'scalblnl' instead."; _obsoletePosixFunctions["ualarm"] = "Obsolete function 'ualarm' called. It is recommended to use 'timer_create', 'timer_delete', 'timer_getoverrun', 'timer_gettime' or 'timer_settime' instead."; _obsoletePosixFunctions["tmpnam"] = "Obsolete function 'tmpnam' called. It is recommended to use 'tmpfile', 'mkstemp' or 'mkdtemp' instead."; _obsoletePosixFunctions["tmpnam_r"] = "Obsolete function 'tmpnam_r' called. It is recommended to use 'tmpfile', 'mkstemp' or 'mkdtemp' instead."; _obsoleteStandardFunctions["gets"] = "Obsolete function 'gets' called. It is recommended to use the function 'fgets' instead.\n" "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 function 'fgets' instead."; _obsoleteC99Functions["alloca"] = "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)."; _obsoleteC99Functions["asctime"] = "Obsolete function 'asctime' called. It is recommended to use the function 'strftime' instead."; // ctime is obsolete - it's not threadsafe. but there is no good replacement. //_obsoleteC99Functions["ctime"] = "Obsolete function 'ctime' called. It is recommended to use the function 'strftime' instead."; } void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckObsoleteFunctions c(0, settings, errorLogger); std::map::const_iterator it(_obsoletePosixFunctions.begin()), itend(_obsoletePosixFunctions.end()); for (; it!=itend; ++it) { c.reportError(0, Severity::style, "obsoleteFunctions"+it->first, it->second); } } static std::string myName() { return "Obsolete functions"; } std::string classInfo() const { std::string info = "Warn if any of these obsolete functions are used:\n"; std::map::const_iterator it(_obsoletePosixFunctions.begin()), itend(_obsoletePosixFunctions.end()); for (; it!=itend; ++it) { info += "* " + it->first + "\n"; } return info; } }; /// @} //--------------------------------------------------------------------------- #endif // checkobsoletefunctionsH cppcheck-1.66/lib/checkother.cpp000066400000000000000000005032361236713773000166410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mathlib.h" #include "symboldatabase.h" #include // fabs() #include #include // find_if() //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckOther instance; } static bool astIsFloat(const Token *tok, bool unknown) { if (tok->astOperand1() && tok->str() != "?" && astIsFloat(tok->astOperand1(),unknown)) return true; if (tok->astOperand2() && astIsFloat(tok->astOperand2(), unknown)) return true; if (tok->isNumber()) return MathLib::isFloat(tok->str()); if (tok->isName()) { // TODO: check function calls, struct members, arrays, etc also if (!tok->variable()) return unknown; return Token::findmatch(tok->variable()->typeStartToken(), "float|double", tok->variable()->typeEndToken()->next(), 0) != nullptr; } if (tok->isOp()) return false; return unknown; } static bool isConstExpression(const Token *tok, const std::set &constFunctions) { if (!tok) return true; if (tok->isName() && tok->next()->str() == "(") { if (!tok->function() && !Token::Match(tok->previous(), ".|::") && constFunctions.find(tok->str()) == constFunctions.end()) return false; else if (tok->function() && !tok->function()->isConst) return false; } if (tok->type() == Token::eIncDecOp) return false; // bailout when we see ({..}) if (tok->str() == "{") return false; return isConstExpression(tok->astOperand1(),constFunctions) && isConstExpression(tok->astOperand2(),constFunctions); } bool isSameExpression(const Token *tok1, const Token *tok2, const std::set &constFunctions) { if (tok1 == nullptr && tok2 == nullptr) return true; if (tok1 == nullptr || tok2 == nullptr) return false; 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->str() != tok2->str() || tok1->varId() != tok2->varId()) return false; if (tok1->str() == "." && tok1->originalName() != tok2->originalName()) return false; if (tok1->isExpandedMacro() || tok2->isExpandedMacro()) return false; if (tok1->isName() && tok1->next()->str() == "(") { if (!tok1->function() && !Token::Match(tok1->previous(), ".|::") && constFunctions.find(tok1->str()) == constFunctions.end() && !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, "%var% <") && tok1->next()->link()) || (Token::Match(tok2, "%var% <") && 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 = tok1->next()->link(); const Token *end2 = tok2->next()->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->type() == Token::eIncDecOp || tok1->isAssignmentOp()) 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->isName() || t1->str() == "*")) { t1 = t1->next(); t2 = t2->next(); } if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") return false; } // bailout when we see ({..}) if (tok1->str() == "{") return false; bool noncommuative_equals = isSameExpression(tok1->astOperand1(), tok2->astOperand1(), constFunctions); noncommuative_equals = noncommuative_equals && isSameExpression(tok1->astOperand2(), tok2->astOperand2(), constFunctions); if (noncommuative_equals) return true; bool commutative = tok1->astOperand1() && tok1->astOperand2() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); bool commuative_equals = commutative && isSameExpression(tok1->astOperand2(), tok2->astOperand1(), constFunctions); commuative_equals = commuative_equals && isSameExpression(tok1->astOperand1(), tok2->astOperand2(), constFunctions); return commuative_equals; } static bool isOppositeCond(const Token * const cond1, const Token * const cond2, const std::set &constFunctions) { if (!cond1 || !cond1->isComparisonOp() || !cond2 || !cond2->isComparisonOp()) return false; const std::string &comp1 = cond1->str(); // condition found .. get comparator std::string comp2; if (isSameExpression(cond1->astOperand1(), cond2->astOperand1(), constFunctions) && isSameExpression(cond1->astOperand2(), cond2->astOperand2(), constFunctions)) { comp2 = cond2->str(); } else if (isSameExpression(cond1->astOperand1(), cond2->astOperand2(), constFunctions) && isSameExpression(cond1->astOperand2(), cond2->astOperand1(), constFunctions)) { comp2 = cond2->str(); if (comp2[0] == '>') comp2[0] = '<'; else if (comp2[0] == '<') comp2[0] = '>'; } // is condition opposite? return ((comp1 == "==" && comp2 == "!=") || (comp1 == "!=" && comp2 == "==") || (comp1 == "<" && comp2 == ">=") || (comp1 == "<" && comp2 == ">") || (comp1 == "<=" && comp2 == ">") || (comp1 == ">" && comp2 == "<=") || (comp1 == ">" && comp2 == "<") || (comp1 == ">=" && comp2 == "<")); } //---------------------------------------------------------------------------------- // 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 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("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()) { 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 (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 (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"; } } 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." ); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::clarifyCalculation() { if (!_settings->isEnabled("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()->isArithmeticalOp() || !tok->astOperand1()->isCalculation()) 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() == ")" || tok2->str() == "?") break; } if (tok2 && tok2->str() == "?") clarifyCalculationError(tok, tok->astOperand1()->str()); } } } 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))' // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' //--------------------------------------------------------------------------- void CheckOther::clarifyCondition() { if (!_settings->isEnabled("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, "( %var% [=&|^]")) { for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (tok2->type() == Token::eComparisonOp) { // This might be a template if (!isC && tok2->link()) break; clarifyConditionError(tok, tok->strAt(2) == "=", false); break; } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".") break; } } } } // using boolean result in bitwise operation ! x [&|^] 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, "%comp%|!")) { if (tok->link()) // don't write false positives when templates are used continue; const Token *tok2 = tok->next(); // Todo: There are false positives if '(' if encountered. It // is assumed there is something like '(char *)&..' and therefore // it bails out. if (Token::Match(tok2, "(|&")) continue; while (tok2 && (tok2->isName() || tok2->isNumber() || Token::Match(tok2,".|(|["))) { if (Token::Match(tok2, "(|[")) tok2 = tok2->link(); tok2 = tok2->next(); } if (Token::Match(tok2, "[&|^]")) { // don't write false positives when templates are used if (Token::Match(tok2, "&|* ,|>") || Token::simpleMatch(tok2->previous(), "const &")) continue; // #3609 - CWinTraits::.. if (!isC && Token::Match(tok->previous(), "%var% <")) { const Token *tok3 = tok2; while (Token::Match(tok3, "[&|^] %var%")) tok3 = tok3->tokAt(2); if (Token::Match(tok3, ",|>")) continue; } clarifyConditionError(tok,false,true); } } } } } void CheckOther::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); } //--------------------------------------------------------------------------- // Clarify (meaningless) statements like *foo++; with parentheses. //--------------------------------------------------------------------------- void CheckOther::clarifyStatement() { if (!_settings->isEnabled("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, "* %var%") && tok->astOperand1()) { const Token *tok2=tok->previous(); while (tok2 && tok2->str() == "*") tok2 = tok2->previous(); if (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)++;'?"); } void CheckOther::checkSuspiciousSemicolon() { if (!_settings->inconclusive || !_settings->isEnabled("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.", true); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::warningOldStylePointerCast() { // Only valid on C++ code if (!_settings->isEnabled("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]; for (const Token* tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const| %type% * ) (| %var%") && !Token::Match(tok, "( const| %type% * ) (| new")) continue; if (tok->strAt(1) == "const") tok = tok->next(); if (tok->strAt(4) == "const") continue; // Is "type" a class? if (_tokenizer->getSymbolDatabase()->isClassOrStruct(tok->strAt(1))) 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."); } //--------------------------------------------------------------------------- // float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation //--------------------------------------------------------------------------- static std::string analyzeType(const Token* tok) { if (tok->str() == "double") { if (tok->isLong()) return "long double"; else return "double"; } if (tok->str() == "float") return "float"; if (Token::Match(tok, "int|long|short|char|size_t")) return "integer"; return ""; } void CheckOther::invalidPointerCast() { if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability")) 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* toTok = 0; const Token* nextTok = 0; // Find cast if (Token::Match(tok, "( const| %type% const| * )") || Token::Match(tok, "( const| %type% %type% const| * )")) { toTok = tok->next(); nextTok = tok->link()->next(); if (nextTok && nextTok->str() == "(") nextTok = nextTok->next(); } else if (Token::Match(tok, "reinterpret_cast < const| %type% const| * > (") || Token::Match(tok, "reinterpret_cast < const| %type% %type% const| * > (")) { nextTok = tok->tokAt(5); while (nextTok->str() != "(") nextTok = nextTok->next(); nextTok = nextTok->next(); toTok = tok->tokAt(2); } if (toTok && toTok->str() == "const") toTok = toTok->next(); if (!nextTok || !toTok || !toTok->isStandardType()) continue; // Find casted variable const Variable *var = nullptr; bool allocation = false; bool ref = false; if (Token::Match(nextTok, "new %type%")) allocation = true; else if (Token::Match(nextTok, "%var% !![")) var = nextTok->variable(); else if (Token::Match(nextTok, "& %var%") && !Token::Match(nextTok->tokAt(2), "(|[")) { var = nextTok->next()->variable(); ref = true; } const Token* fromTok = 0; if (allocation) { fromTok = nextTok->next(); } else { if (!var || (!ref && !var->isPointer() && !var->isArray()) || (ref && (var->isPointer() || var->isArray()))) continue; fromTok = var->typeStartToken(); } while (Token::Match(fromTok, "static|const")) fromTok = fromTok->next(); if (!fromTok->isStandardType()) continue; std::string fromType = analyzeType(fromTok); std::string toType = analyzeType(toTok); if (fromType != toType && !fromType.empty() && !toType.empty() && (toType != "integer" || _settings->isEnabled("portability")) && (toTok->str() != "char" || _settings->inconclusive)) invalidPointerCastError(tok, fromType, toType, toTok->str() == "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."); else reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to char* is not portable due to different binary data representations on different platforms.", true); } else reportError(tok, Severity::warning, "invalidPointerCast", "Casting between " + from + "* and " + to + "* which have an incompatible binary data representation."); } //--------------------------------------------------------------------------- // 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."); } //--------------------------------------------------------------------------- // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- static bool nonLocal(const Variable* var) { return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference(); } 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++); if (i == container.end()) break; } else ++i; } } static void eraseMemberAssignments(const unsigned int varId, 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); } } } void CheckOther::checkRedundantAssignment() { const bool performance = _settings->isEnabled("performance"); const bool warning = _settings->isEnabled("warning"); if (!warning && !performance) return; 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 = 0; for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (tok == writtenArgumentsEnd) writtenArgumentsEnd = 0; 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")) { varAssignments.clear(); memAssignments.clear(); } else if (tok->type() == Token::eVariable && !Token::Match(tok,"%var% (")) { // 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, "%var%|::|.")) { startToken = startToken->previous(); if (Token::Match(startToken, "%var% . %var%")) membervars[startToken->varId()].insert(startToken->tokAt(2)->varId()); } std::map::iterator it = varAssignments.find(tok->varId()); if (tok->next()->isAssignmentOp() && Token::Match(startToken, "[;{}]")) { // Assignment if (it != varAssignments.end()) { bool error = true; // Ensure that variable is not used on right side for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") break; else if (tok2->varId() == tok->varId()) error = false; else if (Token::Match(tok2, "%var% (") && nonLocal(tok->variable())) { // Called function might use the variable const Function* const func = tok2->function(); const Variable* const var = tok->variable(); if (!var || var->isGlobal() || var->isReference() || ((!func || func->nestedIn) && tok2->strAt(-1) != ".")) // Global variable, or member function error = false; } } if (error) { if (scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok) && warning) redundantAssignmentInSwitchError(it->second, tok, tok->str()); else if (performance) { const bool nonlocal = nonLocal(it->second->variable()); if (_settings->inconclusive || !nonlocal) // see #5089 - report inconclusive only when requested redundantAssignmentError(it->second, tok, tok->str(), nonlocal); // Inconclusive for non-local variables } } 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()->type() == Token::eIncDecOp || (tok->previous()->type() == 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, "%var% (")) { // 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")) { if (tok->str() == "memset" && initialized.find(param1->varId()) == initialized.end() && param1->variable() && param1->variable()->isLocal() && param1->variable()->isArray()) initialized.insert(param1->varId()); else if (memAssignments.find(param1->varId()) == memAssignments.end()) memAssignments[param1->varId()] = tok; else { const std::map::iterator it = memAssignments.find(param1->varId()); if (warning && scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok)) redundantCopyInSwitchError(it->second, tok, param1->str()); else if (performance) 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) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::performance, "redundantCopy", "Buffer '" + var + "' is being written before its old content has been used."); } void CheckOther::redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::warning, "redundantCopyInSwitch", "Buffer '" + var + "' is being written before its old content has been used. 'break;' missing?"); } void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); if (inconclusive) reportError(callstack, Severity::performance, "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.", true); else reportError(callstack, Severity::performance, "redundantAssignment", "Variable '" + var + "' is reassigned a value before the old one has been used."); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::warning, "redundantAssignInSwitch", "Variable '" + var + "' is reassigned a value before the old one has been used. 'break;' missing?"); } //--------------------------------------------------------------------------- // 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, "%var% (") || Token::Match(tok, "break|continue|return|exit|goto|throw")) return true; return false; } void CheckOther::checkRedundantAssignmentInSwitch() { if (!_settings->isEnabled("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% ;") && tok2->varId() != 0) { 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% = %var% %or%|& %num% ;") && tok2->varId() != 0 && tok2->varId() == tok2->tokAt(2)->varId()) { std::string bitOp = tok2->strAt(3) + tok2->strAt(4); std::map::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?"); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::checkSwitchCaseFallThrough() { if (!(_settings->isEnabled("style") && _settings->experimental)) 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::eSwitch || !i->classStart) // Find the beginning of a switch continue; // Check the contents of the switch statement std::stack > ifnest; bool justbreak = true; bool firstcase = true; for (const Token *tok2 = i->classStart; tok2 != i->classEnd; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "if (")) { tok2 = tok2->next()->link()->next(); ifnest.push(std::make_pair(tok2->link(), false)); justbreak = false; } else if (Token::simpleMatch(tok2, "while (")) { tok2 = tok2->next()->link()->next(); if (tok2->link()) // skip over "do { } while ( ) ;" case tok2 = tok2->link(); justbreak = false; } else if (Token::simpleMatch(tok2, "do {")) { tok2 = tok2->next()->link(); justbreak = false; } else if (Token::simpleMatch(tok2, "for (")) { tok2 = tok2->next()->link()->next()->link(); justbreak = false; } else if (Token::simpleMatch(tok2, "switch (")) { // skip over nested switch, we'll come to that soon tok2 = tok2->next()->link()->next()->link(); } else if (Token::Match(tok2, "break|continue|return|exit|goto|throw")) { 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() == "}") { if (!ifnest.empty() && tok2 == ifnest.top().first) { if (tok2->next()->str() == "else") { tok2 = tok2->tokAt(2); ifnest.pop(); ifnest.push(std::make_pair(tok2->link(), justbreak)); justbreak = false; } else { justbreak &= ifnest.top().second; ifnest.pop(); } } } else if (tok2->str() != ";") { justbreak = false; } } } } void CheckOther::switchCaseFallThrough(const Token *tok) { reportError(tok, Severity::style, "switchCaseFallThrough", "Switch falls through case without comment. 'break;' missing?"); } //--------------------------------------------------------------------------- // Check for statements like case A||B: in switch() //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousCaseInSwitch() { if (!_settings->inconclusive || !_settings->isEnabled("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; 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?", true); } //--------------------------------------------------------------------------- // if (x == 1) // x == 0; // <- suspicious equality comparison. //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousEqualityComparison() { if (!_settings->isEnabled("warning") || !_settings->inconclusive) return; for (const Token *tok = _tokenizer->tokens(); tok; 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(), "%var% ==")) suspiciousEqualityComparisonError(openParen->tokAt(2)); if (Token::Match(closeParen->tokAt(-2), "== %any%")) suspiciousEqualityComparisonError(closeParen->tokAt(-2)); // Equality comparisons with 0 are simplified to negation. For instance, // (x == 0) is simplified to (!x), so also check for suspicious negation // in the initialization or increment-decrement parts of the for() loop. // For example: // for (!i; i < 10; i++) if (Token::Match(openParen->next(), "! %var%")) suspiciousEqualityComparisonError(openParen->next()); if (Token::Match(closeParen->tokAt(-2), "! %var%")) 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, "[;{}] *| %var% == %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?", true); } //--------------------------------------------------------------------------- // 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; } static bool checkIntRelation(const std::string &op, const MathLib::bigint value1, const MathLib::bigint 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 static 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: { T ret = std::min(value1, value2); if ((ret - (T)1) < ret) return ret - (T)1; else if ((ret / (T)2) < ret) return ret / (T)2; else if ((ret * (T)2) < ret) return ret * (T)2; return ret; } case 2: return value1; case 3: return (value1 + value2) / (T)2; case 4: return value2; case 5: { T ret = std::max(value1, value2); if ((ret + (T)1) > ret) return ret + (T)1; else if ((ret / (T)2) > ret) return ret / (T)2; else if ((ret * (T)2) > ret) return ret * (T)2; return ret; } }; return 0; } void CheckOther::checkIncorrectLogicOperator() { bool style = _settings->isEnabled("style"); bool warning = _settings->isEnabled("warning"); if (!style && !warning) return; 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%")) { // 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(); if (!comp1 || !comp1->isComparisonOp() || !comp1->astOperand1() || !comp1->astOperand2()) continue; if (!comp2 || !comp2->isComparisonOp() || !comp2->astOperand1() || !comp2->astOperand2()) continue; std::string op1, value1; const Token *expr1; if (comp1->astOperand1()->isLiteral()) { op1 = invertOperatorForOperandSwap(comp1->str()); value1 = comp1->astOperand1()->str(); expr1 = comp1->astOperand2(); } else if (comp1->astOperand2()->isLiteral()) { op1 = comp1->str(); value1 = comp1->astOperand2()->str(); expr1 = comp1->astOperand1(); } else { continue; } std::string op2, value2; const Token *expr2; if (comp2->astOperand1()->isLiteral()) { op2 = invertOperatorForOperandSwap(comp2->str()); value2 = comp2->astOperand1()->str(); expr2 = comp2->astOperand2(); } else if (comp2->astOperand2()->isLiteral()) { op2 = comp2->str(); value2 = comp2->astOperand2()->str(); expr2 = comp2->astOperand1(); } else { continue; } // Only float and int values are currently handled if (!MathLib::isInt(value1) && !MathLib::isFloat(value1)) continue; if (!MathLib::isInt(value2) && !MathLib::isFloat(value2)) continue; if (isSameExpression(comp1, comp2, _settings->library.functionpure)) continue; // same expressions => only report that there are same expressions if (!isSameExpression(expr1, expr2, _settings->library.functionpure)) 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; // 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 d1 = MathLib::toDoubleNumber(value1); const double d2 = MathLib::toDoubleNumber(value2); const double testvalue = getvalue(test, d1, d2); result1 = checkFloatRelation(op1, testvalue, d1); result2 = checkFloatRelation(op2, testvalue, d2); } else { const MathLib::bigint i1 = MathLib::toLongNumber(value1); const MathLib::bigint i2 = MathLib::toLongNumber(value2); const MathLib::bigint testvalue = getvalue(test, i1, i2); result1 = checkIntRelation(op1, testvalue, i1); result2 = checkIntRelation(op2, testvalue, i2); } 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 = (expr1->isName() ? expr1->str() : "EXPR") + " " + op1 + " " + value1; const std::string cond2str = (expr2->isName() ? expr2->str() : "EXPR") + " " + op2 + " " + value2; if (warning && (alwaysTrue || alwaysFalse)) { const std::string text = cond1str + " " + tok->str() + " " + cond2str; incorrectLogicOperatorError(tok, text, alwaysTrue); } else if (style && secondTrue) { const std::string text = "If " + cond1str + ", the comparison " + cond2str + " is always " + (secondTrue ? "true" : "false") + "."; redundantConditionError(tok, text); } else if (style && 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 " + (firstTrue ? "true" : "false") + "."; redundantConditionError(tok, text); } } } } } void CheckOther::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always) { 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?"); 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?"); } void CheckOther::redundantConditionError(const Token *tok, const std::string &text) { reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text); } //--------------------------------------------------------------------------- // strtol(str, 0, radix) <- radix must be 0 or 2-36 //--------------------------------------------------------------------------- void CheckOther::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, "%var% ( !!)")) continue; const std::string& functionName = tok->str(); int argnr = 1; const Token *argtok = tok->tokAt(2); while (argtok && argtok->str() != ")") { if (Token::Match(argtok,"%num% [,)]")) { if (MathLib::isInt(argtok->str()) && !_settings->library.isargvalid(functionName, argnr, MathLib::toLongNumber(argtok->str()))) invalidFunctionArgError(argtok,functionName,argnr,_settings->library.validarg(functionName,argnr)); } else { const Token *top = argtok; while (top->astParent() && top->astParent()->str() != "," && top->astParent() != tok->next()) top = top->astParent(); if (top->isComparisonOp() || Token::Match(top, "%oror%|&&")) { if (_settings->library.isboolargbad(functionName, argnr)) invalidFunctionArgBoolError(top, functionName, argnr); // Are the values 0 and 1 valid? else if (!_settings->library.isargvalid(functionName, argnr, 0)) invalidFunctionArgError(top, functionName, argnr, _settings->library.validarg(functionName,argnr)); else if (!_settings->library.isargvalid(functionName, argnr, 1)) invalidFunctionArgError(top, functionName, argnr, _settings->library.validarg(functionName,argnr)); } } argnr++; argtok = argtok->nextArgument(); } } } // 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|swprintf ( %var% ,")) varid = tok->tokAt(2)->varId(); else if (Token::Match(tok, "sprintf|snprintf|swprintf ( %var% . %var% ,")) varid = tok->tokAt(4)->varId(); if (varid == 0) continue; // goto next argument const Token *tok2 = tok->tokAt(2)->nextArgument(); if (tok->str() == "snprintf" || tok->str() == "swprintf") { // Jump over second parameter for snprintf and swprintf tok2 = tok2->nextArgument(); if (!tok2) continue; } // is any source buffer overlapping the target buffer? do { if (Token::Match(tok2, "%varid% [,)]", varid)) { sprintfOverlappingDataError(tok2, tok2->str()); break; } } while (nullptr != (tok2 = tok2->nextArgument())); } } void CheckOther::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.\""); } void CheckOther::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr) { std::ostringstream errmsg; errmsg << "Invalid " << functionName << "() argument nr " << argnr; if (!tok) ; else if (tok->isNumber()) errmsg << ". The value is " << tok->str() << " but the valid values are '" << validstr << "'."; else if (tok->isComparisonOp()) errmsg << ". The value is 0 or 1 (comparison result) but the valid values are '" << validstr << "'."; reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str()); } void CheckOther::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()); } //--------------------------------------------------------------------------- // 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("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const Token* secondBreak = 0; const Token* labelName = 0; 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 = 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, "%var% (") && _settings->library.isnoreturn(tok->str()) && tok->strAt(-1) != ".") { 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. bool inconclusive = secondBreak && (secondBreak->linenr()-1 > secondBreak->previous()->linenr()); if (secondBreak && (_settings->inconclusive || !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 *scope = Token::findsimplematch(secondBreak, "{"); if (scope) { for (const Token *tokIter = scope; tokIter != scope->link() && tokIter; tokIter = tokIter->next()) { if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) { labelInFollowingLoop = true; break; } } } } if (!labelInFollowingLoop) 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.", 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.", inconclusive); } //--------------------------------------------------------------------------- // Check for unsigned divisions //--------------------------------------------------------------------------- bool CheckOther::isUnsigned(const Variable* var) const { return (var && var->typeStartToken()->isUnsigned() && !var->isPointer() && !var->isArray() && _tokenizer->sizeOfType(var->typeStartToken()) >= _settings->sizeof_int); } bool CheckOther::isSigned(const Variable* var) { return (var && !var->typeStartToken()->isUnsigned() && Token::Match(var->typeEndToken(), "int|char|short|long") && !var->isPointer() && !var->isArray()); } void CheckOther::checkUnsignedDivision() { bool warning = _settings->isEnabled("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]; const Token* ifTok = 0; // Check for "ivar / uvar" and "uvar / ivar" for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "[).]")) // Don't check members or casted variables continue; if (Token::Match(tok->next(), "%var% / %num%")) { if (tok->strAt(3)[0] == '-' && isUnsigned(tok->next()->variable())) { udivError(tok->next(), false); } } else if (Token::Match(tok->next(), "%num% / %var%")) { if (tok->strAt(1)[0] == '-' && isUnsigned(tok->tokAt(3)->variable())) { udivError(tok->next(), false); } } else if (Token::Match(tok->next(), "%var% / %var%") && _settings->inconclusive && warning && !ifTok) { const Variable* var1 = tok->next()->variable(); const Variable* var2 = tok->tokAt(3)->variable(); if ((isUnsigned(var1) && isSigned(var2)) || (isUnsigned(var2) && isSigned(var1))) { udivError(tok->next(), true); } } else if (!ifTok && Token::simpleMatch(tok, "if (")) ifTok = tok->next()->link()->next()->link(); else if (ifTok == tok) ifTok = 0; } } } void CheckOther::udivError(const Token *tok, bool inconclusive) { if (inconclusive) reportError(tok, Severity::warning, "udivError", "Division with signed and unsigned operators. The result might be wrong.", true); else reportError(tok, Severity::error, "udivError", "Unsigned division. The result will be wrong."); } //--------------------------------------------------------------------------- // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //--------------------------------------------------------------------------- void CheckOther::checkMemsetZeroBytes() { if (!_settings->isEnabled("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::simpleMatch(tok, "memset (")) { const Token* lastParamTok = tok->next()->link()->previous(); if (lastParamTok->str() == "0") memsetZeroBytesError(tok, tok->strAt(2)); } } } } 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 + " 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); } void CheckOther::checkMemsetInvalid2ndParam() { const bool portability = _settings->isEnabled("portability"); const bool warning = _settings->isEnabled("warning"); 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->next(); tok && (tok != scope->classEnd); tok = tok->next()) { if (Token::simpleMatch(tok, "memset (")) { const Token* firstParamTok = tok->tokAt(2); if (!firstParamTok) continue; const Token* secondParamTok = firstParamTok->nextArgument(); if (!secondParamTok) continue; // Second parameter is zero literal, i.e. 0.0f if (Token::Match(secondParamTok, "%num% ,") && MathLib::isNullValue(secondParamTok->str())) continue; const Token *top = secondParamTok; while (top->astParent() && top->astParent()->str() != ",") top = top->astParent(); // Check if second parameter is a float variable or a float literal != 0.0f if (portability && astIsFloat(top,false)) { memsetFloatError(secondParamTok, top->expressionString()); } else if (warning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range const long long int value = MathLib::toLongNumber(secondParamTok->str()); if (value < -128 || value > 255) memsetValueOutOfRangeError(secondParamTok, secondParamTok->str()); } } } } } void CheckOther::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); } void CheckOther::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); } //--------------------------------------------------------------------------- // Check scope of variables.. //--------------------------------------------------------------------------- void CheckOther::checkVariableScope() { if (!_settings->isEnabled("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() && !var->typeStartToken()->next()->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 (tok->str() == "{" || tok->str() == ";" || tok->str() == "}") 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->type() != Token::eString && tok->type() != Token::eChar && !tok->isBoolean()) continue; } bool reduce = true; bool used = false; // Don't warn about unused variables for (; tok != var->scope()->classEnd; tok = tok->next()) { if (tok->str() == "{" && tok->scope() != tok->previous()->scope()) { 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; } 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 = 0; 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 != 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 = 0; 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% = %any%", 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."); } void CheckOther::checkCommaSeparatedReturn() { // This is experimental for now. See #5076 if (!_settings->experimental) return; if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "return") { while (tok && tok->str() != ";") { if (Token::Match(tok, "[([{<]") && tok->link()) 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."); } //--------------------------------------------------------------------------- // Check for constant function parameters //--------------------------------------------------------------------------- void CheckOther::checkConstantFunctionParameter() { if (!_settings->isEnabled("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->isConst() || var->isPointer() || var->isArray() || var->isReference()) continue; const Token* const tok = var->typeStartToken(); // 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, "std :: string|wstring !!::")) { passedByValueError(tok, var->name()); } else if (Token::Match(tok, "std :: %type% <") && !Token::simpleMatch(tok->linkAt(3), "> ::")) { passedByValueError(tok, var->name()); } else if (var->type()) { // Check if type is a struct or class. passedByValueError(tok, var->name()); } } } 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 by value. It could be passed " "as a (const) reference which is usually faster and recommended in C++."); } //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- static bool isChar(const Variable* var) { return (var && !var->isPointer() && !var->isArray() && var->typeStartToken()->str() == "char"); } static bool isSignedChar(const Variable* var) { return (isChar(var) && !var->typeStartToken()->isUnsigned()); } static bool astIsSignedChar(const Token *tok) { if (!tok) return false; if (tok->str() == "*" && tok->astOperand1() && !tok->astOperand2()) { const Variable *var = tok->astOperand1()->variable(); if (!var || !var->isPointer()) return false; const Token *type = var->typeStartToken(); while (type && type->str() == "const") type = type->next(); return (type && type->str() == "char" && !type->isUnsigned()); } return isSignedChar(tok->variable()); } void CheckOther::checkCharVariable() { if (!_settings->isEnabled("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 (Token::Match(tok, "!!. %var% [") && astIsSignedChar(tok->tokAt(2)->astOperand2())) { const Variable* arrayvar = tok->next()->variable(); const MathLib::bigint arraysize = (arrayvar && arrayvar->isArray()) ? arrayvar->dimension(0U) : 0; if (arraysize > 0x80) charArrayIndexError(tok->next()); } else if (Token::Match(tok, "[&|^]")) { const Token *tok2; if (tok->astOperand1() && astIsSignedChar(tok->astOperand1())) tok2 = tok->astOperand2(); else if (tok->astOperand2() && astIsSignedChar(tok->astOperand2())) tok2 = tok->astOperand1(); else continue; // Don't care about address-of operator if (!tok->astOperand2()) continue; // it's ok with a bitwise and where the other operand is 0xff or less.. if (tok->str() == "&" && tok2 && tok2->isNumber() && MathLib::isGreater("0x100", tok2->str())) continue; // is the result stored in a short|int|long? if (tok->astParent() && tok->astParent()->str() == "=") { const Token *eq = tok->astParent(); const Token *lhs = eq->astOperand1(); if (lhs && lhs->str() == "*" && !lhs->astOperand2()) lhs = lhs->astOperand1(); while (lhs && lhs->str() == ".") lhs = lhs->astOperand2(); if (!lhs || !lhs->isName()) continue; const Variable *var = lhs->variable(); if (var && Token::Match(var->typeStartToken(), "short|int|long")) charBitOpError(tok); // This is an error.. } } } } } void CheckOther::charArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, "charArrayIndex", "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."); } 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."); } //--------------------------------------------------------------------------- // Incomplete statement.. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteStatement() { if (!_settings->isEnabled("warning")) 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(); // C++11 struct/array/etc initialization in initializer list else if (Token::Match(tok->previous(), "%var% {") && !Token::findsimplematch(tok,";",tok->link())) tok = tok->link(); // C++11 vector initialization / return { .. } else if (Token::Match(tok,"> %var% {") || Token::Match(tok, "[;{}] return {")) tok = tok->linkAt(2); // C++11 initialize set in initalizer list : [,:] std::set{1} [{,] else if (Token::simpleMatch(tok,"> {") && tok->link()) tok = tok->next()->link(); else if (Token::Match(tok, "[;{}] %str%|%num%")) { // No warning if numeric constant is followed by a "." or "," if (Token::Match(tok->next(), "%num% [,.]")) 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; 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."); } //--------------------------------------------------------------------------- // str plus char //--------------------------------------------------------------------------- void CheckOther::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()->type() == Token::eString)) { // string literal... if (tok->astOperand2() && (tok->astOperand2()->type() == Token::eChar || isChar(tok->astOperand2()->variable()))) // added to char variable or char constant strPlusCharError(tok); } } } } } void CheckOther::strPlusCharError(const Token *tok) { reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type 'char' is added to a string literal."); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::checkZeroDivision() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "div|ldiv|lldiv|imaxdiv ( %num% , %num% )") && MathLib::isInt(tok->strAt(4)) && MathLib::toLongNumber(tok->strAt(4)) == 0L) { if (tok->str() == "div") { if (tok->strAt(-1) == ".") continue; if (tok->variable() || tok->function()) continue; } zerodivError(tok,false); } else if (Token::Match(tok, "[/%]") && tok->astOperand2()) { // Value flow.. const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); if (value) { if (!_settings->inconclusive && value->inconclusive) continue; if (value->condition == nullptr) zerodivError(tok, value->inconclusive); else if (_settings->isEnabled("warning")) zerodivcondError(value->condition,tok,value->inconclusive); } } } } void CheckOther::zerodivError(const Token *tok, bool inconclusive) { reportError(tok, Severity::error, "zerodiv", "Division by zero.", inconclusive); } void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv, bool inconclusive) { std::list callstack; while (Token::Match(tokcond, "(|%oror%|&&")) tokcond = tokcond->next(); if (tokcond && tokdiv) { callstack.push_back(tokcond); callstack.push_back(tokdiv); } std::string condition; if (!tokcond) { // getErrorMessages } else if (Token::Match(tokcond, "%num% <|<=")) { condition = tokcond->strAt(2) + ((tokcond->strAt(1) == "<") ? ">" : ">=") + tokcond->str(); } else if (tokcond->isComparisonOp()) { condition = tokcond->expressionString(); } else { if (tokcond->str() == "!") condition = tokcond->next()->str() + "==0"; else condition = tokcond->str() + "!=0"; } const std::string linenr(MathLib::toString(tokdiv ? tokdiv->linenr() : 0)); reportError(callstack, Severity::warning, "zerodivcond", "Either the condition '"+condition+"' is useless or there is division by zero at line " + linenr + ".", inconclusive); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- /** @brief Check for NaN (not-a-number) in an arithmetic expression * @note 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."); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::checkMathFunctions() { 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 (tok->strAt(-1) != "." && Token::Match(tok, "log|logf|logl|log10|log10f|log10l ( %num% )")) { bool isNegative = MathLib::isNegative(tok->strAt(2)); bool isInt = MathLib::isInt(tok->strAt(2)); bool isFloat = MathLib::isFloat(tok->strAt(2)); if (isNegative && isInt && MathLib::toLongNumber(tok->strAt(2)) <= 0) { mathfunctionCallError(tok); // case log(-2) } else if (isNegative && isFloat && MathLib::toDoubleNumber(tok->strAt(2)) <= 0.) { mathfunctionCallError(tok); // case log(-2.0) } else if (!isNegative && isFloat && MathLib::toDoubleNumber(tok->strAt(2)) <= 0.) { mathfunctionCallError(tok); // case log(0.0) } else if (!isNegative && isInt && MathLib::toLongNumber(tok->strAt(2)) <= 0) { mathfunctionCallError(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% )") && std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0) { mathfunctionCallError(tok); } // sqrt( x ): if x is negative the result is undefined else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )") && MathLib::isNegative(tok->strAt(2))) { mathfunctionCallError(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% )") && MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) { 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 (Token::Match(tok, "fmod|fmodf|fmodl ( %any%")) { const Token* nextArg = tok->tokAt(2)->nextArgument(); if (nextArg && nextArg->isNumber() && MathLib::isNullValue(nextArg->str())) mathfunctionCallError(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% )") && MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) { mathfunctionCallError(tok, 2); } } } } void CheckOther::mathfunctionCallError(const Token *tok, const unsigned int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->strAt(2) + " to " + tok->str() + "() leads to undefined result."); else if (numParam == 2) reportError(tok, Severity::error, "wrongmathcall", "Passing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() leads to undefined result."); } else reportError(tok, Severity::error, "wrongmathcall", "Passing value '#' to #() leads to undefined result."); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::checkMisusedScopedObject() { // Skip this check for .c files if (_tokenizer->isC()) { 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 (Token::Match(tok, "[;{}] %var% (") && Token::Match(tok->linkAt(2), ") ; !!}") && symbolDatabase->isClassOrStruct(tok->next()->str()) && (!tok->next()->function() || // is not a function on this scope (tok->next()->function() && 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::error, "unusedScopedObject", "Instance of '" + varname + "' object is destroyed immediately."); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckOther::checkIncorrectStringCompare() { if (!_settings->isEnabled("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 (Token::Match(tok, "%var% (") && (Token::Match(tok->tokAt(2), "%str% &&") || Token::Match(tok->next()->link()->tokAt(-2), "&& %str% )")) && (tok->str().find("assert")+6U==tok->str().size() || tok->str().find("ASSERT")+6U==tok->str().size())) tok = tok->next()->link(); if (Token::simpleMatch(tok, ". substr (") && Token::Match(tok->tokAt(3)->nextArgument(), "%num% )")) { MathLib::bigint clen = MathLib::toLongNumber(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 != (int)slen) { incorrectStringCompareError(tok->next(), "substr", begin->strAt(-1)); } } else if (Token::Match(end, "==|!= %str% !!+")) { std::size_t slen = Token::getStrLength(end->next()); if (clen != (int)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 CheckOther::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 + "()."); } void CheckOther::incorrectStringBooleanError(const Token *tok, const std::string& string) { reportError(tok, Severity::warning, "incorrectStringBooleanError", "Conversion of string literal " + string + " to bool always evaluates to true."); } //----------------------------------------------------------------------------- // 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("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); // 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) { 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 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.", true); } //----------------------------------------------------------------------------- // Check for a free() of an invalid address // char* p = malloc(100); // free(p + 10); //----------------------------------------------------------------------------- void CheckOther::checkInvalidFree() { std::map allocatedVariables; for (const Token* tok = _tokenizer->tokens(); tok; 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% = %var% +|-") && tok->varId() == tok->tokAt(2)->varId() && allocatedVariables.find(tok->varId()) != allocatedVariables.end()) { if (_settings->inconclusive) 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% +|- %any%") || Token::Match(tok, "delete [ ] ( %any% +|- %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::iterator alloc1 = allocatedVariables.find(var1); const std::map::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, "%var% (")) { 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.", inconclusive); } //----------------------------------------------------------------------------- // Check for double free // free(p); free(p); //----------------------------------------------------------------------------- void CheckOther::checkDoubleFree() { std::set freedVariables; std::set closeDirVariables; for (const Token* tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Keep track of any variables passed to "free()", "g_free()" or "closedir()", // and report an error if the same variable is passed twice. if (Token::Match(tok, "free|g_free|closedir ( %var% )")) { const unsigned int varId = tok->tokAt(2)->varId(); if (varId) { if (Token::Match(tok, "free|g_free")) { if (freedVariables.find(varId) != freedVariables.end()) doubleFreeError(tok, tok->strAt(2)); else freedVariables.insert(varId); } else if (tok->str() == "closedir") { if (closeDirVariables.find(varId) != closeDirVariables.end()) doubleCloseDirError(tok, tok->strAt(2)); else closeDirVariables.insert(varId); } } } // Keep track of any variables operated on by "delete" or "delete[]" // and report an error if the same variable is delete'd twice. else if (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) { const int varIndex = (tok->strAt(1) == "[") ? 3 : 1; const unsigned int varId = tok->tokAt(varIndex)->varId(); if (varId) { if (freedVariables.find(varId) != freedVariables.end()) doubleFreeError(tok, tok->strAt(varIndex)); else freedVariables.insert(varId); } } // If this scope doesn't return, clear the set of previously freed variables else if (tok->str() == "}" && _tokenizer->IsScopeNoReturn(tok)) { freedVariables.clear(); closeDirVariables.clear(); } // If this scope is a "for" or "while" loop that contains "break" or "continue", // give up on trying to figure out the flow of execution and just clear the set // of previously freed variables. // TODO: There are false negatives. This bailout is only needed when the // loop will exit without free()'ing the memory on the last iteration. else if (tok->str() == "}" && tok->link() && tok->link()->previous() && tok->link()->linkAt(-1) && Token::Match(tok->link()->linkAt(-1)->previous(), "while|for") && Token::findmatch(tok->link()->linkAt(-1), "break|continue ;", tok) != nullptr) { freedVariables.clear(); closeDirVariables.clear(); } // If a variable is passed to a function, remove it from the set of previously freed variables else if (Token::Match(tok, "%var% (") && !Token::Match(tok, "printf|sprintf|snprintf|fprintf|wprintf|swprintf|fwprintf")) { // If this is a new function definition, clear all variables if (Token::simpleMatch(tok->next()->link(), ") {")) { freedVariables.clear(); closeDirVariables.clear(); } // If it is a function call, then clear those variables in its argument list else if (Token::simpleMatch(tok->next()->link(), ") ;")) { for (const Token* tok2 = tok->tokAt(2); tok2 != tok->linkAt(1); tok2 = tok2->next()) { const unsigned int varId = tok2->varId(); if (varId) { freedVariables.erase(varId); closeDirVariables.erase(varId); } } } } // If a pointer is assigned a new value, remove it from the set of previously freed variables else if (Token::Match(tok, "%var% =")) { const unsigned int varId = tok->varId(); if (varId) { freedVariables.erase(varId); closeDirVariables.erase(varId); } } // Any control statements in-between delete, free() or closedir() statements // makes it unclear whether any subsequent statements would be redundant. if (Token::Match(tok, "if|else|for|while|break|continue|goto|return|throw|switch")) { freedVariables.clear(); closeDirVariables.clear(); } } } void CheckOther::doubleFreeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname +"' is freed twice."); } void CheckOther::doubleCloseDirError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "doubleCloseDir", "Directory handle '" + varname +"' closed twice."); } 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); } } } } } //--------------------------------------------------------------------------- // 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) //--------------------------------------------------------------------------- static bool isWithoutSideEffects(const Tokenizer *tokenizer, const Token* tok) { if (!tokenizer->isCPP()) return true; while (tok && tok->astOperand2() && tok->astOperand2()->str() != "(") tok = tok->astOperand2(); if (tok->varId()) { const Variable* var = tok->variable(); return var && (!var->isClass() || var->isPointer() || var->isStlType()); } return true; } void CheckOther::checkDuplicateExpression() { if (!_settings->isEnabled("style")) 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(tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) { if (isWithoutSideEffects(_tokenizer, tok->astOperand1())) { const bool assignment = tok->str() == "="; if (assignment) selfAssignmentError(tok, tok->astOperand1()->expressionString()); else duplicateExpressionError(tok, tok, tok->str()); } } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative if (tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(tok->astOperand2(), tok->astOperand1()->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer, 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(ast1->astOperand1(), tok->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer, 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 (isSameExpression(ast1->astOperand2(), tok->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer, ast1->astOperand2())) duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok->str()); if (!isConstExpression(ast1->astOperand2(), _settings->library.functionpure)) break; ast1 = ast1->astOperand1(); } } } } } } } 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."); } void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "Redundant assignment of '" + varname + "' to itself."); } //--------------------------------------------------------------------------- // Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //--------------------------------------------------------------------------- void CheckOther::checkAlwaysTrueOrFalseStringCompare() { if (!_settings->isEnabled("warning")) return; for (const Token* tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp (")) { if (Token::Match(tok->tokAt(2), "%str% , %str%")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); } else if (Token::Match(tok->tokAt(2), "%var% , %var%")) { 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), "%var% . c_str ( ) , %var% . 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 (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); } } } void CheckOther::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."); } void CheckOther::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."); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckOther::checkSuspiciousStringCompare() { if (!_settings->isEnabled("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->type() != Token::eComparisonOp) continue; const Token* varTok = tok->astOperand1(); const Token* litTok = tok->astOperand2(); if (!varTok || !litTok) // <- failed to create AST for comparison continue; if (varTok->type() == Token::eString || varTok->type() == Token::eNumber) std::swap(varTok, litTok); else if (litTok->type() != Token::eString && litTok->type() != Token::eNumber) 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 = t->astOperand2(); if (t && t->variable() && t->variable()->isPointer()) varTok = t; } } if (varTok->str() == "*") { if (!_tokenizer->isC() || varTok->astOperand2() != nullptr || litTok->type() != Token::eString) continue; varTok = varTok->astOperand1(); } while (varTok && 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(); if (litTok->type() == Token::eString) { if (_tokenizer->isC() || (var && var->isPointer())) suspiciousStringCompareError(tok, varname); } else if (litTok->originalName() == "'\\0'" && var && var->isPointer()) { suspiciousStringCompareError_char(tok, varname); } } } } void CheckOther::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?"); } void CheckOther::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?"); } //----------------------------------------------------------------------------- // 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) evaluates always to false. // // Reference: // - http://www.cplusplus.com/reference/cmath/ //----------------------------------------------------------------------------- void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() { if (!_settings->isEnabled("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 calles with the same variables if (varidLeft != 0 && 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"; reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse", "Comparison of two identical variables with "+functionName+"("+varName+","+varName+") evaluates always 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+"."); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckOther::checkModuloAlwaysTrueFalse() { if (!_settings->isEnabled("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 CheckOther::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 + "."); } //----------------------------------------------------------------------------- // Check for code like: // seteuid(geteuid()) or setuid(getuid()), which first gets and then sets the // (effective) user id to itself. Very often this indicates a copy and paste // error. //----------------------------------------------------------------------------- void CheckOther::redundantGetAndSetUserId() { if (_settings->isEnabled("warning") && _settings->standards.posix) { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "setuid ( getuid ( ) )") || Token::simpleMatch(tok, "seteuid ( geteuid ( ) )") || Token::simpleMatch(tok, "setgid ( getgid ( ) )") || Token::simpleMatch(tok, "setegid ( getegid ( ) )")) { redundantGetAndSetUserIdError(tok); } } } } void CheckOther::redundantGetAndSetUserIdError(const Token *tok) { reportError(tok, Severity::warning, "redundantGetAndSetUserId", "Redundant get and set of user id.\n" "Redundant statement without any effect. First the user id is retrieved" "by get(e)uid() and then set with set(e)uid().", false); } //--------------------------------------------------------------------------- // Check testing sign of unsigned variables and pointers. //--------------------------------------------------------------------------- void CheckOther::checkSignOfUnsignedVariable() { if (!_settings->isEnabled("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 (Token::Match(tok, "%var% <|<= 0") && tok->varId() && !Token::Match(tok->tokAt(3), "+|-")) { // TODO: handle a[10].b , a::b , (unsigned int)x , etc const Token *prev = tok->previous(); while (prev && (prev->isName() || prev->str() == ".")) prev = prev->previous(); if (!Token::Match(prev, "(|&&|%oror%")) continue; const Variable *var = tok->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZeroError(tok, var->name(), inconclusive); else if (var && (var->isPointer() || var->isArray())) pointerLessThanZeroError(tok, inconclusive); } else if (Token::Match(tok, "0 >|>= %var%") && tok->tokAt(2)->varId() && !Token::Match(tok->tokAt(3), "+|-|*|/") && !Token::Match(tok->previous(), "+|-|<<|>>|~")) { const Variable *var = tok->tokAt(2)->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZeroError(tok, var->name(), inconclusive); else if (var && var->isPointer() && !Token::Match(tok->tokAt(3), "[.[(]")) pointerLessThanZeroError(tok, inconclusive); } else if (Token::Match(tok, "0 <= %var%") && tok->tokAt(2)->varId() && !Token::Match(tok->tokAt(3), "+|-|*|/") && !Token::Match(tok->previous(), "+|-|<<|>>|~")) { const Variable *var = tok->tokAt(2)->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedPositiveError(tok, var->name(), inconclusive); else if (var && var->isPointer() && !Token::Match(tok->tokAt(3), "[.[]")) pointerPositiveError(tok, inconclusive); } else if (Token::Match(tok, "%var% >= 0") && tok->varId() && !Token::Match(tok->previous(), "++|--|)|+|-|*|/|~|<<|>>") && !Token::Match(tok->tokAt(3), "+|-")) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->isUnsigned()) unsignedPositiveError(tok, var->name(), inconclusive); else if (var && var->isPointer() && tok->strAt(-1) != "*") pointerPositiveError(tok, 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.", 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."); } } 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.", 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", true); } else { reportError(tok, Severity::style, "unsignedPositive", "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it."); } } 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.", 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("performance") || _tokenizer->isC()) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::size_t i = 0; 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% %var% = ... ; ; 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(), "%var% (")) 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.", true); // since #5618 that check became inconlusive } //--------------------------------------------------------------------------- // Checking for shift by negative values //--------------------------------------------------------------------------- void CheckOther::checkNegativeBitwiseShift() { 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() != "<<" && tok->str() != ">>") continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; // don't warn if lhs is a class. this is an overloaded operator then if (_tokenizer->isCPP()) { const Token *rhs = tok->astOperand1(); while (Token::Match(rhs, "::|.")) rhs = rhs->astOperand2(); if (!rhs) continue; if (!rhs->isNumber() && !rhs->variable()) continue; if (rhs->variable() && (!rhs->variable()->typeStartToken() || !rhs->variable()->typeStartToken()->isStandardType())) continue; } // Get negative rhs value. preferably a value which doesn't have 'condition'. const ValueFlow::Value *value = tok->astOperand2()->getValueLE(-1LL, _settings); if (value) negativeBitwiseShiftError(tok); } } } void CheckOther::negativeBitwiseShiftError(const Token *tok) { reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour"); } //--------------------------------------------------------------------------- // Check for incompletely filled buffers. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteArrayFill() { bool warning = _settings->isEnabled("warning"); bool portability = _settings->isEnabled("portability"); if (!_settings->inconclusive || (!portability && !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|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 != 1 && size != 100 && size != 0) || var->isPointer()) { if (warning) incompleteArrayFillError(tok, var->name(), tok->str(), false); } else if (var->typeStartToken()->str() == "bool" && portability) // 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 + ")'?", 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 + ")'?", true); } void CheckOther::oppositeInnerCondition() { if (!_settings->isEnabled("warning")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { if (scope->type != Scope::eIf) continue; if (!Token::simpleMatch(scope->classDef->linkAt(1), ") {")) continue; bool nonlocal = false; // nonlocal variable used in condition std::set vars; // variables used in condition for (const Token *cond = scope->classDef; cond != scope->classDef->linkAt(1); cond = cond->next()) { if (cond->varId()) { vars.insert(cond->varId()); const Variable *var = cond->variable(); nonlocal |= (var && (!var->isLocal() || var->isStatic()) && !var->isArgument()); // TODO: if var is pointer check what it points at nonlocal |= (var && (var->isPointer() || var->isReference())); } else if (cond->isName()) { // varid is 0. this is possibly a nonlocal variable.. nonlocal |= (cond->astParent() && cond->astParent()->isConstOp()); } } // parse until inner condition is reached.. const Token *ifToken = nullptr; for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "if (")) { ifToken = tok; break; } if (Token::Match(tok,"%type% (") && nonlocal) // function call -> bailout if there are nonlocal variables break; else if ((tok->varId() && vars.find(tok->varId()) != vars.end()) || (!tok->varId() && nonlocal)) { if (Token::Match(tok, "%var% ++|--|=")) break; if (Token::Match(tok,"%var% [")) { const Token *tok2 = tok->linkAt(1); while (Token::simpleMatch(tok2, "] [")) tok2 = tok2->linkAt(1); if (Token::simpleMatch(tok2, "] =")) break; } if (Token::Match(tok->previous(), "++|--|& %var%")) break; if (Token::Match(tok->previous(), "[(,] %var% [,)]")) { // is variable unchanged? default is false.. bool unchanged = false; // locate start parentheses in function call.. unsigned int argumentNumber = 0; const Token *start = tok->previous(); while (start && start->str() == ",") { start = start->astParent(); ++argumentNumber; } start = start ? start->previous() : nullptr; if (start && start->function()) { const Variable *arg = start->function()->getArgumentVar(argumentNumber); if (arg && !arg->isPointer() && !arg->isReference()) unchanged = true; } if (!unchanged) break; } } } if (!ifToken) continue; // Condition.. const Token *cond1 = scope->classDef->next()->astOperand2(); const Token *cond2 = ifToken->next()->astOperand2(); if (isOppositeCond(cond1, cond2, _settings->library.functionpure)) oppositeInnerConditionError(scope->classDef, cond2); } } void CheckOther::oppositeInnerConditionError(const Token *tok1, const Token* tok2) { std::list callstack; callstack.push_back(tok1); callstack.push_back(tok2); reportError(callstack, Severity::warning, "oppositeInnerCondition", "Opposite conditions in nested 'if' blocks lead to a dead code block."); } void CheckOther::checkVarFuncNullUB() { if (!_settings->isEnabled("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 (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; i. */ //--------------------------------------------------------------------------- #ifndef checkotherH #define checkotherH //--------------------------------------------------------------------------- #include "config.h" #include "check.h" class Function; class Variable; /** Is expressions same? */ bool isSameExpression(const Token *tok1, const Token *tok2, const std::set &constFunctions); /// @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.checkUnsignedDivision(); checkOther.checkCharVariable(); checkOther.strPlusChar(); checkOther.checkRedundantAssignment(); checkOther.checkRedundantAssignmentInSwitch(); checkOther.checkSuspiciousCaseInSwitch(); checkOther.checkDuplicateBranch(); checkOther.checkDuplicateExpression(); checkOther.checkUnreachableCode(); checkOther.checkSuspiciousSemicolon(); checkOther.checkVariableScope(); checkOther.clarifyCondition(); // not simplified because ifAssign checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) checkOther.checkIncompleteArrayFill(); checkOther.checkSuspiciousStringCompare(); checkOther.checkVarFuncNullUB(); checkOther.checkNanInArithmeticExpression(); checkOther.checkCommaSeparatedReturn(); } /** @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.oppositeInnerCondition(); checkOther.clarifyCalculation(); checkOther.clarifyStatement(); checkOther.checkConstantFunctionParameter(); checkOther.checkIncompleteStatement(); checkOther.checkCastIntToCharAndBack(); checkOther.invalidFunctionUsage(); checkOther.checkZeroDivision(); checkOther.checkMathFunctions(); checkOther.redundantGetAndSetUserId(); checkOther.checkIncorrectLogicOperator(); checkOther.checkMisusedScopedObject(); checkOther.checkMemsetZeroBytes(); checkOther.checkMemsetInvalid2ndParam(); checkOther.checkIncorrectStringCompare(); checkOther.checkSwitchCaseFallThrough(); checkOther.checkAlwaysTrueOrFalseStringCompare(); checkOther.checkModuloAlwaysTrueFalse(); checkOther.checkPipeParameterSize(); checkOther.checkInvalidFree(); checkOther.checkDoubleFree(); checkOther.checkRedundantCopy(); checkOther.checkNegativeBitwiseShift(); checkOther.checkSuspiciousEqualityComparison(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); } /** To check the dead code in a program, which is inaccessible due to the counter-conditions check in nested-if statements **/ void oppositeInnerCondition(); /** @brief Clarify calculation for ".. a * b ? .." */ void clarifyCalculation(); /** @brief Suspicious condition (assignment+comparison) */ void clarifyCondition(); /** @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 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 unsigned division */ void checkUnsignedDivision(); /** @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 constant function parameter */ void checkConstantFunctionParameter(); /** @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 str plus char (unusual pointer arithmetic) */ void strPlusChar(); /** @brief %Check zero division*/ void checkZeroDivision(); /** @brief %Check zero division / useless condition */ void checkZeroDivisionOrUselessCondition(); /** @brief Check for NaN (not-a-number) in an arithmetic expression */ void checkNanInArithmeticExpression(); /** @brief %Check for parameters given to math function that do not make sense*/ void checkMathFunctions(); /** @brief % Check for seteuid(geteuid()) or setuid(getuid())*/ void redundantGetAndSetUserId(); /** @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 switch case fall through without comment */ void checkSwitchCaseFallThrough(); /** @brief %Check for testing for mutual exclusion over ||*/ void checkIncorrectLogicOperator(); /** @brief %Check for objects that are destroyed immediately */ void checkMisusedScopedObject(); /** @brief %Check for filling zero bytes with memset() */ void checkMemsetZeroBytes(); /** @brief %Check for invalid 2nd parameter of memset() */ void checkMemsetInvalid2ndParam(); /** @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 where multiple if have the same expression (e.g "if (a) { } else if (a) { }") */ void checkDuplicateIf(); /** @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 suspicious code that compares string literals for equality */ void checkAlwaysTrueOrFalseStringCompare(); /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ void checkModuloAlwaysTrueFalse(); /** @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 double free or double close operations */ void checkDoubleFree(); void doubleFreeError(const Token *tok, const std::string &varname); /** @brief %Check for code creating redundant copies */ void checkRedundantCopy(); /** @brief %Check for bitwise operation 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(); private: bool isUnsigned(const Variable *var) const; static bool isSigned(const Variable *var); // Error messages.. void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, 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 oppositeInnerConditionError(const Token *tok1, const Token* tok2); void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyConditionError(const Token *tok, bool assign, bool boolop); void clarifyStatementError(const Token* tok); void redundantGetAndSetUserIdError(const Token *tok); void cstyleCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive); void sprintfOverlappingDataError(const Token *tok, const std::string &varname); void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr); void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); void udivError(const Token *tok, bool inconclusive); void passedByValueError(const Token *tok, const std::string &parname); void constStatementError(const Token *tok, const std::string &type); void charArrayIndexError(const Token *tok); void charBitOpError(const Token *tok); void variableScopeError(const Token *tok, const std::string &varname); void strPlusCharError(const Token *tok); void zerodivError(const Token *tok, bool inconclusive); void zerodivcondError(const Token *tokcond, const Token *tokdiv, bool inconclusive); void nanInArithmeticExpressionError(const Token *tok); void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1); 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 switchCaseFallThrough(const Token *tok); void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); void suspiciousEqualityComparisonError(const Token* tok); void selfAssignmentError(const Token *tok, const std::string &varname); void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always); void redundantConditionError(const Token *tok, const std::string &text); void misusedScopeObjectError(const Token *tok, const std::string &varname); void memsetZeroBytesError(const Token *tok, const std::string &varname); void memsetFloatError(const Token *tok, const std::string &var_value); void memsetValueOutOfRangeError(const Token *tok, const std::string &value); void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); void incorrectStringBooleanError(const Token *tok, const std::string& string); void duplicateIfError(const Token *tok1, const Token *tok2); void duplicateBranchError(const Token *tok1, const Token *tok2); void duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op); 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 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 doubleCloseDirError(const Token *tok, const std::string &varname); void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); void negativeBitwiseShiftError(const Token *tok); 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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckOther c(0, settings, errorLogger); // error c.sprintfOverlappingDataError(0, "varname"); c.invalidFunctionArgError(0, "func_name", 1, "1-4"); c.invalidFunctionArgBoolError(0, "func_name", 1); c.udivError(0, false); c.zerodivError(0, false); c.zerodivcondError(0,0,false); c.mathfunctionCallError(0); c.misusedScopeObjectError(NULL, "varname"); c.doubleFreeError(0, "varname"); c.invalidPointerCastError(0, "float", "double", false); c.negativeBitwiseShiftError(0); c.checkPipeParameterSizeError(0, "varname", "dimension"); //performance c.redundantCopyError(0, "varname"); c.redundantCopyError(0, 0, "var"); c.redundantAssignmentError(0, 0, "var", false); // style/warning c.checkComparisonFunctionIsAlwaysTrueOrFalseError(0,"isless","varName",false); c.checkCastIntToCharAndBackError(0,"func_name"); c.oppositeInnerConditionError(0, 0); c.cstyleCastError(0); c.passedByValueError(0, "parametername"); c.constStatementError(0, "type"); c.charArrayIndexError(0); c.charBitOpError(0); c.variableScopeError(0, "varname"); c.strPlusCharError(0); c.redundantAssignmentInSwitchError(0, 0, "var"); c.redundantCopyInSwitchError(0, 0, "var"); c.switchCaseFallThrough(0); c.suspiciousCaseInSwitchError(0, "||"); c.suspiciousEqualityComparisonError(0); c.selfAssignmentError(0, "varname"); c.incorrectLogicOperatorError(0, "foo > 3 && foo < 4", true); c.redundantConditionError(0, "If x > 11 the condition x > 10 is always true."); c.memsetZeroBytesError(0, "varname"); c.memsetFloatError(0, "varname"); c.memsetValueOutOfRangeError(0, "varname"); c.clarifyCalculationError(0, "+"); c.clarifyConditionError(0, true, false); c.clarifyStatementError(0); c.incorrectStringCompareError(0, "substr", "\"Hello World\""); c.suspiciousStringCompareError(0, "foo"); c.suspiciousStringCompareError_char(0, "foo"); c.incorrectStringBooleanError(0, "\"Hello World\""); c.duplicateBranchError(0, 0); c.duplicateExpressionError(0, 0, "&&"); c.alwaysTrueFalseStringCompareError(0, "str1", "str2"); c.alwaysTrueStringVariableCompareError(0, "varname1", "varname2"); c.duplicateBreakError(0, false); c.unreachableCodeError(0, false); c.unsignedLessThanZeroError(0, "varname", false); c.unsignedPositiveError(0, "varname", false); c.pointerLessThanZeroError(0, false); c.pointerPositiveError(0, false); c.SuspiciousSemicolonError(0); c.moduloAlwaysTrueFalseError(0, "1"); c.incompleteArrayFillError(0, "buffer", "memset", false); c.varFuncNullUBError(0); c.nanInArithmeticExpressionError(0); c.commaSeparatedReturnError(0); } static std::string myName() { return "Other"; } std::string classInfo() const { return "Other checks\n" // error "* Assigning bool value to pointer (converting bool value to address)\n" "* division with zero\n" "* scoped object destroyed immediately after construction\n" "* assignment in an assert statement\n" "* incorrect length arguments for 'substr' and 'strncmp'\n" "* free() or delete of an invalid memory location\n" "* double free() or double closedir()\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" "* invalid input values for functions\n" // warning "* either division by zero or useless condition\n" "* memset() with a value out of range as the 2nd parameter\n" // performance "* redundant data copying for const variable\n" "* subsequent assignment or copying to a variable or buffer\n" // portability "* memset() with a float as the 2nd parameter\n" // style "* Find dead code which is inaccessible due to the counter-conditions check in nested if statements\n" "* C-style pointer cast in cpp file\n" "* casting between incompatible pointer types\n" "* redundant if\n" "* [[CheckUnsignedDivision|unsigned division]]\n" "* passing parameter by value\n" "* [[IncompleteStatement|Incomplete statement]]\n" "* [[charvar|check how signed char variables are used]]\n" "* variable scope can be limited\n" "* condition that is always true/false\n" "* unusual pointer arithmetic. For example: \"abc\" + 'd'\n" "* redundant assignment in a switch statement\n" "* redundant pre/post operation in a switch statement\n" "* redundant bitwise operation in a switch statement\n" "* redundant strcpy in a switch statement\n" "* assignment of a variable to itself\n" "* Suspicious case labels in switch()\n" "* Suspicious equality comparisons\n" "* mutual exclusion over || always evaluating to true\n" "* Comparison of values leading always to true or false\n" "* Clarify calculation with parentheses\n" "* suspicious condition (assignment+comparison)\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" "* duplicate break statement\n" "* unreachable code\n" "* testing if unsigned variable is negative\n" "* testing is unsigned variable is positive\n" "* Suspicious use of ; at the end of 'if/for/while' statement.\n" "* Comparisons of modulo results that are always true/false.\n" "* Array filled incompletely using memset/memcpy/memmove.\n" "* redundant get and set function of user id (--std=posix).\n" "* Passing NULL pointer to function with variable number of arguments leads to UB on some platforms.\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"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkotherH cppcheck-1.66/lib/checkpostfixoperator.cpp000066400000000000000000000063161236713773000207650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "symboldatabase.h" //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckPostfixOperator instance; } void CheckPostfixOperator::postfixOperator() { if (!_settings->isEnabled("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."); } cppcheck-1.66/lib/checkpostfixoperator.h000066400000000000000000000047541236713773000204360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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(0, settings, errorLogger); c.postfixOperatorError(0); } 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.66/lib/checksizeof.cpp000066400000000000000000000373711236713773000170210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "symboldatabase.h" #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckSizeof instance; } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckSizeof::checkSizeofForNumericParameter() { if (!_settings->isEnabled("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."); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckSizeof::checkSizeofForArrayParameter() { 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(); } if (varTok->varId() > 0) { const Variable *var = varTok->variable(); if (var && var->isArray() && var->isArgument()) { sizeofForArrayParameterError(tok); } } } } } } void CheckSizeof::sizeofForArrayParameterError(const Token *tok) { reportError(tok, Severity::error, "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)." ); } void CheckSizeof::checkSizeofForPointerSize() { if (!_settings->isEnabled("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 *tokVar; const Token *variable; 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, "[*;{}] %var% = malloc|alloca (")) { variable = tok->next(); tokVar = tok->tokAt(5); } else if (Token::Match(tok, "[*;{}] %var% = calloc (")) { variable = tok->next(); tokVar = tok->tokAt(5)->nextArgument(); } else if (Token::simpleMatch(tok, "memset (")) { variable = tok->tokAt(2); tokVar = variable->tokAt(2)->nextArgument(); // The following tests can be inconclusive in case the variable in sizeof // is constant string by intention } else if (!_settings->inconclusive) { continue; } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (")) { variable = tok->tokAt(2); variable2 = variable->nextArgument(); if (!variable2) continue; tokVar = variable2->nextArgument(); } else { continue; } // Ensure the variables are in the symbol database // Also ensure the variables are pointers // Only keep variables which are pointers const Variable *var = variable->variable(); if (!var || !var->isPointer() || var->isArray()) { variable = 0; } if (variable2) { var = variable2->variable(); if (!var || !var->isPointer() || var->isArray()) { variable2 = 0; } } // If there are no pointer variable at this point, there is // no need to continue if (variable == 0 && variable2 == 0) { continue; } // Jump to the next sizeof token in the function and in the parameter // This is to allow generic operations with sizeof for (; tokVar && tokVar->str() != ")" && tokVar->str() != "," && tokVar->str() != "sizeof"; tokVar = tokVar->next()) {} // Now check for the sizeof usage. Once here, everything using sizeof(varid) or sizeof(&varid) // looks suspicious // Do it for first variable if (variable && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable->varId()) || Token::Match(tokVar, "sizeof &| %varid%", variable->varId()))) { sizeofForPointerError(variable, variable->str()); } else if (variable2 && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable2->varId()) || Token::Match(tokVar, "sizeof &| %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 + ")'.", true); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckSizeof::sizeofsizeof() { if (!_settings->isEnabled("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)'"); } //----------------------------------------------------------------------------- void CheckSizeof::sizeofCalculation() { if (!_settings->isEnabled("warning")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { const Token *argument = tok->next()->astOperand2(); if (argument && argument->isCalculation() && (!argument->isExpandedMacro() || _settings->inconclusive)) sizeofCalculationError(argument, argument->isExpandedMacro()); } } } void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof().", inconclusive); } //----------------------------------------------------------------------------- // Check for code like sizeof()*sizeof() or sizeof(ptr)/value //----------------------------------------------------------------------------- void CheckSizeof::suspiciousSizeofCalculation() { if (!_settings->isEnabled("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.", 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.", true); } void CheckSizeof::sizeofVoid() { if (!_settings->isEnabled("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::Match(tok, "sizeof ( * %var% )") && tok->tokAt(3)->variable() && (Token::Match(tok->tokAt(3)->variable()->typeStartToken(), "void * !!*")) && (!tok->tokAt(3)->variable()->isArray())) { // sizeof(*p) where p is of type "void*" sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); } else if (Token::Match(tok, "%var% +|-|++|--") || Token::Match(tok, "+|-|++|-- %var%")) { // Arithmetic operations on variable of type "void*" int index = (tok->isName()) ? 0 : 1; const Variable* var = tok->tokAt(index)->variable(); if (var && Token::Match(var->typeStartToken(), "void * !!*")) { std::string varname = tok->strAt(index); // In case this 'void *' var is a member then go back to the main object const Token* tok2 = tok->tokAt(index); if (index == 0) { bool isMember = false; while (Token::simpleMatch(tok2->previous(), ".")) { isMember = true; if (Token::simpleMatch(tok2->tokAt(-2), ")")) tok2 = tok2->linkAt(-2); else if (Token::simpleMatch(tok2->tokAt(-2), "]")) tok2 = tok2->linkAt(-2)->previous(); else tok2 = tok2->tokAt(-2); } if (isMember) { // Get 'struct.member' complete name (without spaces) varname = tok2->stringifyList(tok->next()); varname.erase(std::remove_if(varname.begin(), varname.end(), static_cast(std::isspace)), varname.end()); } } // Check for cast on operations with '+|-' if (Token::Match(tok, "%var% +|-")) { // Check for cast expression if (Token::simpleMatch(tok2->previous(), ")") && !Token::Match(tok2->previous()->link(), "( const| void *")) continue; if (tok2->strAt(-1) == "&") // Check for reference operator continue; } arithOperationsOnVoidPointerError(tok, varname, var->typeStartToken()->stringifyList(var->typeEndToken()->next())); } } } } 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); } 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); } 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); } cppcheck-1.66/lib/checksizeof.h000066400000000000000000000115201236713773000164520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" class Function; class Variable; /// @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*, const Settings*, 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 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(0, settings, errorLogger); c.sizeofForArrayParameterError(0); c.sizeofForPointerError(0, "varname"); c.sizeofForNumericParameterError(0); c.sizeofsizeofError(0); c.sizeofCalculationError(0, false); c.multiplySizeofError(0); c.divideSizeofError(0); c.sizeofVoidError(0); c.sizeofDereferencedVoidPointerError(0, "varname"); c.arithOperationsOnVoidPointerError(0, "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.66/lib/checkstl.cpp000066400000000000000000002234461236713773000163240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "executionpath.h" #include "symboldatabase.h" #include "checknullpointer.h" #include // Register this check class (by creating a static instance of it) namespace { CheckStl instance; } // Error message for bad iterator usage.. void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) { reportError(tok, Severity::error, "invalidIterator1", "Invalid iterator: " + iteratorName); } 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 + "'."); } // Error message used when dereferencing an iterator that has been erased.. void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername) { 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."); } 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."); } } static const Token *skipMembers(const Token *tok) { while (Token::Match(tok, "%var% .")) tok = tok->tokAt(2); return tok; } void CheckStl::iterators() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Using same iterator against different containers. // for (it = foo.begin(); it != bar.end(); ++it) 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|auto")) continue; if (var->typeEndToken()->str() == "auto" && !Token::Match(var->typeEndToken(), "auto %var% ; %var% = %var% . begin|end ( )")) continue; 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) continue; } // the validIterator flag says if the iterator has a valid value or not bool validIterator = Token::Match(var->nameToken()->next(), "[(=]"); const Scope* invalidationScope = 0; // The container this iterator can be used with const Variable* container = 0; const Scope* containerAssignScope = 0; // When "validatingToken" is reached the validIterator is set to true const Token* validatingToken = 0; const Token* eraseToken = 0; // 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) container = 0; // We don't know which containers might be used with the iterator if (tok2 == validatingToken) validIterator = true; // Is iterator compared against different container? if (Token::Match(tok2, "%varid% !=|== %var% . end|rend|cend|crend ( )", iteratorId) && container && tok2->tokAt(2)->varId() != container->declarationId()) { iteratorsError(tok2, container->name(), tok2->strAt(2)); tok2 = tok2->tokAt(6); } // Is the iterator used in a insert/erase operation? else if (Token::Match(tok2, "%var% . 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 (container && tok2->varId() != container->declarationId()) { // 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; // Show error message, mismatching iterator is used. iteratorsError(tok2, container->name(), tok2->str()); } // invalidate the iterator if it is erased else if (tok2->strAt(2) == "erase" && (tok2->strAt(4) != "*" || (container && tok2->varId() == container->declarationId()))) { 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% = %var% .", iteratorId) && Token::simpleMatch(skipMembers(tok2->tokAt(2)), "erase (")) { // the returned iterator is valid validatingToken = tok2->linkAt(5); tok2 = tok2->tokAt(5); } // Reassign the iterator else if (Token::Match(tok2, "%varid% = %var% . begin|rbegin|cbegin|crbegin|find (", iteratorId)) { validatingToken = tok2->linkAt(5); container = tok2->tokAt(2)->variable(); containerAssignScope = tok2->scope(); // skip ahead tok2 = tok2->tokAt(5); } // Reassign the iterator else if (Token::Match(tok2, "%varid% = %any%", 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)); tok2 = tok2->next(); } else if (!validIterator && Token::Match(tok2, "%varid% . %var%", iteratorId)) { dereferenceErasedError(eraseToken, tok2, tok2->str()); tok2 = tok2->tokAt(2); } // bailout handling. Assume that the iterator becomes valid if we see return/break. // TODO: better handling else if (Token::Match(tok2, "return|break")) { validatingToken = Token::findsimplematch(tok2->next(), ";"); } // bailout handling. Assume that the iterator becomes valid if we see else. // TODO: better handling else if (tok2 && 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."); } void CheckStl::mismatchingContainers() { static const char* const algorithm2_strings[] = { // func(begin1, end1 "adjacent_find", "all_of", "any_of", "binary_search", "copy", "copy_if", "count", "count_if", "equal", "equal_range", "find", "find_if", "find_if_not", "for_each", "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", "none_of", "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", "search_n", "shuffle", "sort", "sort_heap", "stable_partition", "stable_sort", "swap_ranges", "transform", "unique", "unique_copy", "upper_bound" }; static const char* const algorithm22_strings[] = { // func(begin1, end1, begin2, end2 "find_end", "find_first_of", "includes", "lexicographical_compare", "merge", "partial_sort_copy", "search", "set_difference", "set_intersection", "set_symmetric_difference", "set_union" }; static const char* const algorithm1x1_strings[] = { // func(begin1, x, end1 "inplace_merge", "nth_element", "partial_sort", "rotate", "rotate_copy" }; static const std::set algorithm2(algorithm2_strings, &algorithm2_strings[sizeof(algorithm2_strings) / sizeof(*algorithm2_strings)]); static const std::set algorithm22(algorithm22_strings, &algorithm22_strings[sizeof(algorithm22_strings) / sizeof(*algorithm22_strings)]); static const std::set algorithm1x1(algorithm1x1_strings, &algorithm1x1_strings[sizeof(algorithm1x1_strings) / sizeof(*algorithm1x1_strings)]); static const std::string iteratorBeginFuncPattern = "begin|cbegin|rbegin|crbegin"; static const std::string iteratorEndFuncPattern = "end|cend|rend|crend"; static const std::string pattern1x1_1 = "%var% . " + iteratorBeginFuncPattern + " ( ) , "; static const std::string pattern1x1_2 = "%var% . " + iteratorEndFuncPattern + " ( ) ,|)"; static const std::string pattern2 = pattern1x1_1 + pattern1x1_2; // 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, "std :: %type% ( !!)")) continue; const Token* arg1 = tok->tokAt(4); // TODO: If iterator variables are used instead then there are false negatives. if (Token::Match(arg1, pattern2.c_str()) && algorithm2.find(tok->strAt(2)) != algorithm2.end()) { if (arg1->str() != arg1->strAt(6)) { mismatchingContainersError(arg1); } } else if (algorithm22.find(tok->strAt(2)) != algorithm22.end()) { if (Token::Match(arg1, pattern2.c_str()) && arg1->str() != arg1->strAt(6)) mismatchingContainersError(arg1); // Find third parameter const Token* arg3 = arg1; for (unsigned int i = 0; i < 2 && arg3; i++) arg3 = arg3->nextArgument(); if (Token::Match(arg3, pattern2.c_str()) && arg3->str() != arg3->strAt(6)) mismatchingContainersError(arg3); } else if (Token::Match(arg1, pattern1x1_1.c_str()) && algorithm1x1.find(tok->strAt(2)) != algorithm1x1.end()) { // Find third parameter const Token *arg3 = arg1->tokAt(6)->nextArgument(); if (Token::Match(arg3, pattern1x1_2.c_str())) { if (arg1->str() != arg3->str()) { mismatchingContainersError(arg1); } } } tok = arg1->linkAt(-1); } } } void CheckStl::stlOutOfBounds() { // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_bounded_container [] = { "array", "basic_string", "deque", "string", "vector", "wstring" }; 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) || !tok) continue; if (i->type == Scope::eFor) tok = Token::findsimplematch(tok->tokAt(2), ";"); else tok = tok->next(); // check if the for loop condition is wrong if (Token::Match(tok, ";|( %var% <= %var% . size|length ( ) ;|)|%oror%")) { // Is it a vector? const Variable *container = tok->tokAt(3)->variable(); if (!container) continue; if (!container->isStlType(stl_bounded_container)) continue; // variable id for loop variable. const unsigned int numId = tok->next()->varId(); // variable id for the container variable const unsigned int declarationId = container->declarationId(); for (const Token *tok3 = tok->tokAt(8); tok3 && tok3 != i->classEnd; tok3 = tok3->next()) { if (tok3->varId() == declarationId) { if (Token::Match(tok3->next(), ". size|length ( )")) break; else if (Token::Match(tok3->next(), "[ %varid% ]", numId)) stlOutOfBoundsError(tok3, tok3->strAt(2), tok3->str(), false); else if (Token::Match(tok3->next(), ". at ( %varid% )", numId)) stlOutOfBoundsError(tok3, tok3->strAt(4), tok3->str(), true); } } break; } } } // Error message for bad iterator usage.. 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."); else reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + "[" + num + "] is out of bounds."); } /** * @brief %Check for invalid iterator usage after erase/insert/etc */ class EraseCheckLoop : public ExecutionPath { public: static void checkScope(CheckStl *checkStl, const Token *it) { const Token *tok = it; // Search for the start of the loop body.. while (0 != (tok = tok->next())) { if (tok->str() == "(") tok = tok->link(); else if (tok->str() == ")") break; // reassigning iterator in loop head else if (Token::Match(tok, "%var% =") && tok->str() == it->str()) break; } if (! Token::simpleMatch(tok, ") {")) return; EraseCheckLoop c(checkStl, it->varId(), it); std::list checks; checks.push_back(c.copy()); ExecutionPath::checkScope(tok->tokAt(2), checks); c.end(checks, tok->link()); while (!checks.empty()) { delete checks.back(); checks.pop_back(); } } private: /** Startup constructor */ EraseCheckLoop(Check *o, unsigned int varid, const Token* usetoken) : ExecutionPath(o, varid), eraseToken(0), useToken(usetoken) { } /** @brief token where iterator is erased (non-zero => the iterator is invalid) */ const Token *eraseToken; /** @brief name of the iterator */ const Token* useToken; /** @brief Copy this check. Called from the ExecutionPath baseclass. */ ExecutionPath *copy() { return new EraseCheckLoop(*this); } /** @brief is another execution path equal? */ bool is_equal(const ExecutionPath *e) const { const EraseCheckLoop *c = static_cast(e); return (eraseToken == c->eraseToken); } /** @brief parse tokens */ const Token *parse(const Token &tok, std::list &checks) const { // bail out if there are assignments. We don't check the assignments properly. if (Token::Match(&tok, "[;{}] %var% =") || Token::Match(&tok, "= %var% ;")) { ExecutionPath::bailOutVar(checks, tok.next()->varId()); } // the loop stops here. Bail out all execution checks that reach // this statement if (Token::Match(&tok, "[;{}] break ;")) { ExecutionPath::bailOut(checks); } // erasing iterator => it is invalidated if (Token::Match(&tok, "erase ( ++|--| %var% )")) { // check if there is a "it = ints.erase(it);" pattern. if so // the it is not invalidated. const Token *token = &tok; while (nullptr != (token = token ? token->previous() : 0)) { if (Token::Match(token, "[;{}]")) break; else if (token->str() == "=") token = 0; } // the it is invalidated by the erase.. if (token) { // get variable id for the iterator unsigned int iteratorId = 0; if (tok.tokAt(2)->isName()) iteratorId = tok.tokAt(2)->varId(); else iteratorId = tok.tokAt(3)->varId(); // invalidate this iterator in the corresponding checks for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) { EraseCheckLoop *c = dynamic_cast(*it); if (c && c->varId == iteratorId) { c->eraseToken = &tok; } } } } // don't skip any tokens. return the token that we received. return &tok; } /** * Parse condition. @sa ExecutionPath::parseCondition * @param tok first token in condition. * @param checks The execution paths. All execution paths in the list are executed in the current scope * @return true => bail out all checking **/ bool parseCondition(const Token &tok, std::list &checks) { // no checking of conditions. (void)tok; (void)checks; return false; } /** @brief going out of scope - all execution paths end */ void end(const std::list &checks, const Token * /*tok*/) const { // check if there are any invalid iterators. If so there is an error. for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) { EraseCheckLoop *c = dynamic_cast(*it); if (c && c->eraseToken) { CheckStl *checkStl = dynamic_cast(c->owner); if (checkStl) { checkStl->dereferenceErasedError(c->eraseToken, c->useToken, c->useToken->str()); } } } } }; void CheckStl::erase() { const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { const Token* const tok = i->classDef; if (tok && i->type == Scope::eFor) { for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { if (Token::Match(tok2, "; %var% !=")) { // Get declaration token for var.. const Variable *variableInfo = tok2->next()->variable(); const Token *decltok = variableInfo ? variableInfo->typeEndToken() : nullptr; // Is variable an iterator? bool isIterator = false; if (decltok && Token::Match(decltok->tokAt(-2), "> :: iterator %varid%", tok2->next()->varId())) isIterator = true; // If tok2->next() is an iterator, check scope if (isIterator) EraseCheckLoop::checkScope(this, tok2->next()); } break; } if (Token::Match(tok2, "%var% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %var% != %var% . end|rend|cend|crend ( )") && tok2->str() == tok2->strAt(8) && tok2->strAt(2) == tok2->strAt(10)) { EraseCheckLoop::checkScope(this, tok2); break; } } } else if (i->type == Scope::eWhile && Token::Match(tok, "while ( %var% !=")) { const Variable* var = tok->tokAt(2)->variable(); if (var && Token::simpleMatch(var->typeEndToken()->tokAt(-2), "> :: iterator")) EraseCheckLoop::checkScope(this, tok->tokAt(2)); } } } 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% [")) { // Variable id for pointer const unsigned int pointerId(tok->varId()); // Variable id for the container variable const unsigned int containerId(tok->tokAt(3)->varId()); if (pointerId == 0 || containerId == 0) continue; 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", containerId)) { 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 = 0; 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 = 0; } // 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()); if (varId == 0) continue; 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 = 0; 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 = ""; } // 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."); } // 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 + "()."); } void CheckStl::stlBoundaries() { 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()) { // Declaring iterator.. if (tok->str() == "<" && Token::Match(tok->previous(), "bitset|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")) { const std::string& container_name(tok->strAt(-1)); if (tok->link()) tok = tok->link(); else continue; if (Token::Match(tok, "> :: iterator|const_iterator %var% =|;")) { const unsigned int iteratorid(tok->tokAt(3)->varId()); if (iteratorid == 0) continue; // Using "iterator < ..." is not allowed const Token* const end = tok->scope()->classEnd; for (const Token *tok2 = tok; tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "!!* %varid% <", iteratorid)) { stlBoundariesError(tok2, container_name); } else if (Token::Match(tok2, "> %varid% !!.", iteratorid)) { stlBoundariesError(tok2, container_name); } } } } } } } // Error message for bad boundary usage.. void CheckStl::stlBoundariesError(const Token *tok, const std::string &container_name) { reportError(tok, Severity::error, "stlBoundaries", "Dangerous iterator comparison using operator< on 'std::" + container_name + "'.\n" "Iterator of container 'std::" + container_name + "' 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."); } static bool if_findCompare(const Token * const tokBack, bool str) { const Token *tok = tokBack; while (tok && tok->str() == ")") { tok = tok->next(); if (Token::Match(tok, ") !!{") && tok->link()->previous() && (Token::Match(tok->link()->previous(),",|==|!=") || tok->link()->previous()->isName())) return true; } if (Token::Match(tok,",|==|!=")) return true; if (str && tok->isComparisonOp()) return true; if (tok->isArithmeticalOp()) // result is used in some calculation return true; // TODO: check if there is a comparison of the result somewhere return false; } void CheckStl::if_find() { const bool warning = _settings->isEnabled("warning"); const bool performance = _settings->isEnabled("performance"); if (!warning && !performance) 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; const Token* tok = i->classDef->next(); if (tok->str() == "if") tok = tok->next(); for (const Token* const end = tok->link(); tok != end; tok = (tok == end) ? end : tok->next()) { if (Token::Match(tok, "&&|(|%oror%")) tok = tok->next(); else continue; while (tok->str() == "(") tok = tok->next(); if (tok->str() == "!") tok = tok->next(); if (Token::Match(tok, "%var% . find (")) { const Variable *var = tok->variable(); if (var) { // Is the variable a std::string or STL container? const Token * decl = var->typeStartToken(); const unsigned int varid = tok->varId(); bool str = Token::Match(decl, "std :: string|wstring &| %varid%", varid); if (if_findCompare(tok->linkAt(3), str)) continue; // stl container if (Token::Match(decl, "std :: %var% < %type% > &| %varid%", varid) && warning) if_findError(tok, false); else if (str && performance) if_findError(tok, true); } } //check also for vector-like or pointer containers else if (Token::Match(tok, "* %var%") || Token::Match(tok, "%var% [")) { // goto %var% if (tok->str() == "*") tok = tok->next(); const Token *tok2 = tok->next(); if (tok2->str() == "[") tok2 = tok2->link()->next(); if (!Token::simpleMatch(tok2, ". find (")) continue; if (if_findCompare(tok2->linkAt(2), false)) continue; const Variable *var = tok->variable(); if (var) { // Is the variable a std::string or STL container? const Token * decl = var->typeStartToken(); const unsigned int varid = tok->varId(); //pretty bad limitation.. but it is there in order to avoid //own implementations of 'find' or any container if (!var->isStlType()) continue; decl = decl->tokAt(2); if (Token::Match(decl, "%var% <")) { decl = decl->tokAt(2); //stl-like if (Token::Match(decl, "std :: %var% < %type% > > &| %varid%", varid) && warning) if_findError(tok, false); //not stl-like, then let's hope it's a pointer or an array else if (Token::Match(decl, "%type% >")) { decl = decl->tokAt(2); if ((Token::Match(decl, "* &| %varid%", varid) || Token::Match(decl, "&| %varid% [ ]| %any% ]| ", varid)) && warning) if_findError(tok, false); } else if (Token::Match(decl, "std :: string|wstring > &| %varid%", varid) && performance) if_findError(tok, true); } else if (decl && decl->str() == "string") { decl = decl->next(); if ((Token::Match(decl, "* &| %varid%", varid) || Token::Match(decl, "&| %varid% [ ]| %any% ]| ", varid)) && performance) if_findError(tok, true); } } } else if (Token::Match(tok, "std :: find|find_if (")) { // check that result is checked properly if (!if_findCompare(tok->linkAt(3), false) && warning) { 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."); else reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find() is an iterator, but it is not properly checked."); } /** * Is container.size() slow? */ static bool isContainerSizeSlow(const Token *tok) { // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* stl_size_slow[] = { "array", "bitset", "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" }; if (!tok) return false; const Variable* var = tok->variable(); return var && var->isStlType(stl_size_slow); } void CheckStl::size() { if (!_settings->isEnabled("performance")) 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, "%var% . %var% . size ( )")) { const Token *tok1 = tok; // get the variable if (tok->strAt(2) != "size") tok1 = tok1->tokAt(2); const Token* const end = tok1->tokAt(5); if (tok1->varId()) { // 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 (isContainerSizeSlow(tok1)) sizeError(tok1); } // 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 (isContainerSizeSlow(tok1)) sizeError(tok1); } // check for using as boolean expression else if ((Token::Match(tok->tokAt(-2), "if|while (") && end->str() == ")") || (tok->previous()->type() == Token::eLogicalOp && Token::Match(end, "&&|)|,|;|%oror%"))) { if (isContainerSizeSlow(tok1)) sizeError(tok1); } } } } } } 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."); } void CheckStl::redundantCondition() { 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, "%var% . find ( %any% ) != %var% . end|rend|cend|crend ( ) ) { %var% . remove|erase ( %any% ) ;")) continue; // Get tokens for the fields %var% 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 "%var%" 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."); } void CheckStl::missingComparison() { if (!_settings->isEnabled("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% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %var% != %var% . end|rend|cend|crend ( ) ; ++| %var% ++| ) {")) continue; // same container if (tok2->strAt(2) != tok2->strAt(10)) break; const unsigned int iteratorId(tok2->varId()); if (iteratorId == 0) break; // 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 = 0; else if (tok3->str() == "break" || tok3->str() == "return") incrementToken = 0; else if (Token::Match(tok3, "%varid% = %var% . 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()); } static bool isLocal(const Token *tok) { const Variable *var = tok->variable(); return var && !var->isStatic() && var->isLocal(); } void CheckStl::string_c_str() { const bool performance = _settings->isEnabled("performance"); // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_string[] = { "string", "u16string", "u32string", "wstring" }; // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_string_stream[] = { "istringstream", "ostringstream", "stringstream", "wstringstream" }; const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); // Find all functions that take std::string as argument std::multimap c_strFuncParam; if (performance) { 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 (const Token* tok = func->argDef->next(); tok != 0; tok = tok->nextArgument()) { numpar++; if (Token::Match(tok, "std :: string|wstring !!&") || Token::Match(tok, "const std :: string|wstring")) 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()->isStlType(stl_string)) { string_c_strThrowError(tok); } else if (Token::Match(tok, "[;{}] %var% = %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% = %var% (") && Token::Match(tok->linkAt(4), ") . c_str|data ( ) ;") && tok->tokAt(3)->function() && Token::Match(tok->tokAt(3)->function()->retDef, "std :: string|wstring %var%")) { const Variable* var = tok->next()->variable(); if (var && var->isPointer()) string_c_strError(tok); } else if (performance && Token::Match(tok, "%var% ( !!)") && 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 tok2 = tok2->previous(); if (tok2 && Token::Match(tok2->tokAt(-4), ". c_str|data ( )")) { const Variable* var = tok2->tokAt(-5)->variable(); if (var && var->isStlType(stl_string)) { string_c_strParam(tok, i->second); } else if (Token::Match(tok2->tokAt(-9), "%var% . 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) { if (Token::Match(tok, "return %var% . c_str|data ( ) ;") && isLocal(tok->next()) && tok->next()->variable() && tok->next()->variable()->isStlType(stl_string)) { string_c_strError(tok); } else if (Token::Match(tok, "return %var% . str ( ) . c_str|data ( ) ;") && isLocal(tok->next()) && tok->next()->variable() && tok->next()->variable()->isStlType(stl_string_stream)) { string_c_strError(tok); } else if (Token::Match(tok, "return std :: string|wstring (") && Token::Match(tok->linkAt(4), ") . c_str|data ( ) ;")) { string_c_strError(tok); } else if (Token::Match(tok, "return %var% (") && Token::Match(tok->linkAt(2), ") . c_str|data ( ) ;")) { const Function* func = tok->next()->function(); if (func && Token::Match(func->tokenDef->tokAt(-3), "std :: string|wstring")) string_c_strError(tok); } else if (Token::simpleMatch(tok, "return (") && Token::Match(tok->next()->link(), ") . c_str|data ( ) ;")) { // Check for "+ localvar" or "+ std::string(" inside the bracket bool is_implicit_std_string = _settings->inconclusive; const Token *search_end = tok->next()->link(); for (const Token *search_tok = tok->tokAt(2); 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()->isStlType(stl_string)) { 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) string_c_strError(tok); } } // Using c_str() to get the return value is redundant if the function returns std::string or const std::string&. else if (performance && (returnType == stdString || returnType == stdStringConstRef)) { if (tok->str() == "return") { const Token* tok2 = Token::findsimplematch(tok->next(), ";"); if (Token::Match(tok2->tokAt(-4), ". c_str|data ( )")) { tok2 = tok2->tokAt(-5); if (tok2->variable() && tok2->variable()->isStlType(stl_string)) { // return var.c_str(); 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."); } 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."); } 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()); } 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; static 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"; 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, "> %var%")) { const Token *tok3 = tok2->tokAt(2); if (Token::Match(tok3, "( new %type%") && hasArrayEndParen(tok3)) { autoPointerArrayError(tok2->next()); } 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, "%var% = %var% ;")) { if (_settings->isEnabled("style")) { 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); } } } } } 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." ); } 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." ); } 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[]'." ); } void CheckStl::uselessCalls() { // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_string[] = { "string", "u16string", "u32string", "wstring" }; // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_containers_with_empty_and_clear[] = { "deque", "forward_list", "list", "map", "multimap", "multiset", "set", "string", "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector", "wstring" }; const bool performance = _settings->isEnabled("performance"); const bool warning = _settings->isEnabled("warning"); if (!performance && !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 (warning && tok->varId() && Token::Match(tok, "%var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %var% [,)]") && tok->varId() == tok->tokAt(4)->varId()) { uselessCallsReturnValueError(tok->tokAt(4), tok->str(), tok->strAt(2)); } else if (performance && tok->varId() && Token::Match(tok, "%var% . swap ( %var% )") && tok->varId() == tok->tokAt(4)->varId()) { uselessCallsSwapError(tok, tok->str()); } else if (performance && Token::Match(tok, "%var% . substr (") && tok->variable() && tok->variable()->isStlType(stl_string)) { 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 (warning && 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()); } 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()); } 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."); else reportError(tok, Severity::performance, "uselessCallsSubstr", "Ineffective call of function 'substr' because it returns a copy of the object. Use operator= instead."); } void CheckStl::uselessCallsEmptyError(const Token *tok) { reportError(tok, Severity::warning, "uselessCallsEmpty", "Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead?"); } 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."); } // Check for iterators being dereferenced before being checked for validity. // E.g. if (*i && i != str.end()) { } void CheckStl::checkDereferenceInvalidIterator() { if (!_settings->isEnabled("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) { const Token* const tok = i->classDef; const Token* startOfCondition = tok->next(); if (i->type == Scope::eDo) startOfCondition = startOfCondition->link()->tokAt(2); 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) != 0; const bool isAndExpression = Token::findsimplematch(startOfCondition, "&&", endOfCondition) != 0; // Look for a check of the validity of an iterator const Token* validityCheckTok = 0; if (!isOrExpression && isAndExpression) { validityCheckTok = Token::findmatch(startOfCondition, "&& %var% != %var% . end|rend|cend|crend ( )", endOfCondition); } else if (isOrExpression && !isAndExpression) { validityCheckTok = Token::findmatch(startOfCondition, "%oror% %var% == %var% . end|rend|cend|crend ( )", endOfCondition); } if (!validityCheckTok) continue; const unsigned int iteratorVarId = validityCheckTok->next()->varId(); if (!iteratorVarId) continue; // 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."); } void CheckStl::readingEmptyStlContainer() { if (!_settings->isEnabled("style")) return; if (!_settings->inconclusive) return; std::set empty_map; // empty std::map-like instances of STL containers std::set empty_nonmap; // empty non-std::map-like instances of STL containers static const char *MAP_STL_CONTAINERS[] = { "map", "multimap", "unordered_map", "unordered_multimap" }; static const char *NONMAP_STL_CONTAINERS[] = { "deque", "forward_list", "list", "multiset", "queue", "set", "stack", "string", "unordered_multiset", "unordered_set", "vector" }; 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|do|}")) { // Loops and end of scope clear the sets. empty_map.clear(); empty_nonmap.clear(); } if (!tok->varId()) continue; // Check whether a variable should be marked as "empty" const Variable* var = tok->variable(); if (var) { bool insert = false; if (var->nameToken() == tok && var->isLocal() && !var->isStatic()) { // Local variable declared insert = !Token::Match(tok->tokAt(1), "[(=]"); // Only if not initialized } else if (Token::Match(tok, "%var% . clear ( ) ;")) { insert = true; } if (insert) { if (var->isStlType(MAP_STL_CONTAINERS)) empty_map.insert(var->declarationId()); else if (var->isStlType(NONMAP_STL_CONTAINERS)) empty_nonmap.insert(var->declarationId()); continue; } } const bool map = empty_map.find(tok->varId()) != empty_map.end(); if (!map && empty_nonmap.find(tok->varId()) == empty_nonmap.end()) continue; // 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) if (map) empty_map.erase(tok->varId()); else empty_nonmap.erase(tok->varId()); } else if (Token::Match(tok, "%var% [")) { // Access through operator[] if (map) { // operator[] inserts an element, if used on a std::map if (tok->strAt(-1) == "=") readingEmptyStlContainerError(tok); empty_map.erase(tok->varId()); } else readingEmptyStlContainerError(tok); } else if (Token::Match(tok, "%var% . %type% (")) { // Member function call if (Token::Match(tok->tokAt(2), "find|at|data|c_str|back|front|empty|top|size|count")) // These functions read from the container readingEmptyStlContainerError(tok); else if (map) empty_map.erase(tok->varId()); else empty_nonmap.erase(tok->varId()); } else if (tok->strAt(-1) == "=") { // Assignment (RHS) readingEmptyStlContainerError(tok); } else { // Unknown usage. Assume it is initialized. if (map) empty_map.erase(tok->varId()); else empty_nonmap.erase(tok->varId()); } } empty_map.clear(); empty_nonmap.clear(); } } void CheckStl::readingEmptyStlContainerError(const Token *tok) { reportError(tok, Severity::style, "reademptycontainer", "Reading from empty STL container", true); } cppcheck-1.66/lib/checkstl.h000066400000000000000000000211011236713773000157510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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.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(); /** * 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(); /** * 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 */ void dereferenceErasedError(const Token* erased, const Token* deref, const std::string &itername); /** @brief Reading from empty stl container */ void readingEmptyStlContainer(); private: /** * Helper function used by the 'erase' function * This function parses a loop * @param it iterator token */ void eraseCheckLoop(const Token *it); 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 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, const std::string &container_name); 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 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(0, settings, errorLogger); c.invalidIteratorError(0, "iterator"); c.iteratorsError(0, "container1", "container2"); c.mismatchingContainersError(0); c.dereferenceErasedError(0, 0, "iter"); c.stlOutOfBoundsError(0, "i", "foo", false); c.invalidIteratorError(0, "push_back|push_front|insert", "iterator"); c.invalidPointerError(0, "push_back", "pointer"); c.stlBoundariesError(0, "container"); c.if_findError(0, false); c.if_findError(0, true); c.string_c_strError(0); c.string_c_strReturn(0); c.string_c_strParam(0, 0); c.sizeError(0); c.missingComparisonError(0, 0); c.redundantIfRemoveError(0); c.autoPointerError(0); c.autoPointerContainerError(0); c.autoPointerArrayError(0); c.uselessCallsReturnValueError(0, "str", "find"); c.uselessCallsSwapError(0, "str"); c.uselessCallsSubstrError(0, false); c.uselessCallsEmptyError(0); c.uselessCallsRemoveError(0, "remove"); c.dereferenceInvalidIteratorError(0, "i"); c.readingEmptyStlContainerError(0); } 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.66/lib/checkuninitvar.cpp000066400000000000000000002272741236713773000175440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mathlib.h" #include "executionpath.h" #include "checknullpointer.h" // CheckNullPointer::parseFunctionCall #include "symboldatabase.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUninitVar instance; } //--------------------------------------------------------------------------- // Skip [ .. ] static const Token * skipBrackets(const Token *tok) { while (tok && tok->str() == "[") tok = tok->link()->next(); return tok; } /// @addtogroup Checks /// @{ /** * @brief %Check that uninitialized variables aren't used (using ExecutionPath) * */ class UninitVar : public ExecutionPath { public: /** Startup constructor */ explicit UninitVar(Check *c, const SymbolDatabase* db, const Library *lib, bool isc) : ExecutionPath(c, 0), symbolDatabase(db), library(lib), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) { } private: /** Create a copy of this check */ ExecutionPath *copy() { return new UninitVar(*this); } /** internal constructor for creating extra checks */ UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, const Library *lib, bool isc) : ExecutionPath(c, v->declarationId()), symbolDatabase(db), library(lib), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) { } /** is other execution path equal? */ bool is_equal(const ExecutionPath *e) const { const UninitVar *c = static_cast(e); return (var == c->var && alloc == c->alloc && strncpy_ == c->strncpy_ && memset_nonzero == c->memset_nonzero); } /** pointer to symbol database */ const SymbolDatabase* symbolDatabase; /** pointer to library */ const Library *library; const bool isC; /** variable for this check */ const Variable* var; /** is this variable allocated? */ bool alloc; /** is this variable initialized with strncpy (not always zero-terminated) */ bool strncpy_; /** is this variable initialized but not zero-terminated (memset) */ bool memset_nonzero; /** allocating pointer. For example : p = malloc(10); */ static void alloc_pointer(std::list &checks, unsigned int varid) { // loop through the checks and perform a allocation if the // variable id matches std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid) { if (c->var->isPointer() && !c->var->isArray()) c->alloc = true; else bailOutVar(checks, varid); break; } } } /** Initializing a pointer value. For example: *p = 0; */ static void init_pointer(std::list &checks, const Token *tok) { const unsigned int varid(tok->varId()); if (!varid) return; // loop through the checks and perform a initialization if the // variable id matches std::list::iterator it = checks.begin(); while (it != checks.end()) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid) { if (c->alloc || c->var->isArray()) { delete c; checks.erase(it++); continue; } else { use_pointer(checks, tok); } } ++it; } } /** Deallocate a pointer. For example: free(p); */ static void dealloc_pointer(std::list &checks, const Token *tok) { const unsigned int varid(tok->varId()); if (!varid) return; // loop through the checks and perform a deallocation if the // variable id matches std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid) { // unallocated pointer variable => error if (c->var->isPointer() && !c->var->isArray() && !c->alloc) { CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); if (checkUninitVar) { checkUninitVar->uninitvarError(tok, c->var->name()); break; } } c->alloc = false; } } } /** * Pointer assignment: p = x; * if p is a pointer and x is an array/pointer then bail out * \param checks all available checks * \param tok1 the "p" token * \param tok2 the "x" token */ static void pointer_assignment(std::list &checks, const Token *tok1, const Token *tok2) { // Variable id for "left hand side" variable const unsigned int varid1(tok1->varId()); if (varid1 == 0) return; // Variable id for "right hand side" variable const unsigned int varid2(tok2->varId()); if (varid2 == 0) return; std::list::const_iterator it; // bail out if first variable is a pointer for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid1 && c->var->isPointer() && !c->var->isArray()) { bailOutVar(checks, varid1); break; } } // bail out if second variable is a array/pointer for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid2 && (c->var->isPointer() || c->var->isArray())) { bailOutVar(checks, varid2); break; } } } /** Initialize an array with strncpy. */ static void init_strncpy(std::list &checks, const Token *tok) { const unsigned int varid(tok->varId()); if (!varid) return; std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid) { c->strncpy_ = true; } } } /** Initialize an array with memset (not zero). */ static void init_memset_nonzero(std::list &checks, const Token *tok) { const unsigned int varid(tok->varId()); if (!varid) return; std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid) { c->memset_nonzero = true; } } } /** * use - called from the use* functions below. * @param checks all available checks * @param tok variable token * @param mode specific behaviour * @return if error is found, true is returned */ static bool use(std::list &checks, const Token *tok, const int mode) { const unsigned int varid(tok->varId()); if (varid == 0) return false; std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == varid) { // mode 0 : the variable is used "directly" // example: .. = var; // it is ok to read the address of an uninitialized array. // it is ok to read the address of an allocated pointer if (mode == 0 && (c->var->isArray() || (c->var->isPointer() && c->alloc))) continue; // mode 2 : reading array data with mem.. function. It's ok if the // array is not null-terminated if (mode == 2 && c->strncpy_) continue; // mode 3 : bad usage of pointer. if it's not a pointer then the usage is ok. // example: ptr->foo(); if (mode == 3 && (!c->var->isPointer() || c->var->isArray())) continue; // mode 4 : using dead pointer is invalid. if (mode == 4 && (!c->var->isPointer() || c->var->isArray() || c->alloc)) continue; // mode 5 : reading uninitialized array or pointer is invalid. if (mode == 5 && (!c->var->isArray() && !c->var->isPointer())) continue; CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); if (checkUninitVar) { if (c->strncpy_ || c->memset_nonzero) { if (!Token::Match(c->var->typeStartToken(), "char|wchar_t")) { continue; } if (Token::Match(tok->next(), "[")) { // Check if it's not being accessed like: 'str[1]' continue; } checkUninitVar->uninitstringError(tok, c->var->name(), c->strncpy_); } else if (c->var->isPointer() && !c->var->isArray() && c->alloc) checkUninitVar->uninitdataError(tok, c->var->name()); else checkUninitVar->uninitvarError(tok, c->var->name()); return true; } } } // No error found return false; } /** * Reading variable. Use this function in situations when it is * invalid to read the data of the variable but not the address. * @param checks all available checks * @param tok variable token * @return if error is found, true is returned */ static bool use(std::list &checks, const Token *tok) { return use(checks, tok, 0); } /** * Reading array elements. If the variable is not an array then the usage is ok. * @param checks all available checks * @param tok variable token */ static void use_array(std::list &checks, const Token *tok) { use(checks, tok, 1); } /** * Reading array elements with a "mem.." function. It's ok if the array is not null-terminated. * @param checks all available checks * @param tok variable token */ static void use_array_mem(std::list &checks, const Token *tok) { use(checks, tok, 2); } /** * Bad pointer usage. If the variable is not a pointer then the usage is ok. * @param checks all available checks * @param tok variable token * @return if error is found, true is returned */ static bool use_pointer(std::list &checks, const Token *tok) { return use(checks, tok, 3); } /** * Using variable.. if it's a dead pointer the usage is invalid. * @param checks all available checks * @param tok variable token * @return if error is found, true is returned */ static bool use_dead_pointer(std::list &checks, const Token *tok) { return use(checks, tok, 4); } /** * Using variable.. reading from uninitialized array or pointer data is invalid. * Example: = x[0]; * @param checks all available checks * @param tok variable token * @return if error is found, true is returned */ static bool use_array_or_pointer_data(std::list &checks, const Token *tok) { return use(checks, tok, 5); } /** * Parse right hand side expression in statement * @param tok2 start token of rhs * @param checks the execution paths */ static void parserhs(const Token *tok2, std::list &checks) { // check variable usages in rhs/index while (nullptr != (tok2 = tok2->next())) { if (Token::Match(tok2, "[;)=]")) break; if (Token::Match(tok2, "%var% (")) break; if (Token::Match(tok2, "%var% <") && Token::simpleMatch(tok2->linkAt(1), "> (")) break; if (tok2->varId() && !Token::Match(tok2->previous(), "&|::") && !Token::simpleMatch(tok2->tokAt(-2), "& (") && tok2->strAt(1) != "=") { // Multiple assignments.. if (Token::Match(tok2->next(), ".|[")) { const Token * tok3 = tok2; while (tok3) { if (Token::Match(tok3->next(), ". %var%")) tok3 = tok3->tokAt(2); else if (tok3->strAt(1) == "[") tok3 = tok3->next()->link(); else break; } if (tok3 && tok3->strAt(1) == "=") continue; } bool foundError; if (tok2->previous()->str() == "*" || tok2->next()->str() == "[") foundError = use_array_or_pointer_data(checks, tok2); else foundError = use(checks, tok2); // prevent duplicate error messages if (foundError) { bailOutVar(checks, tok2->varId()); } } } } /** parse tokens. @sa ExecutionPath::parse */ const Token *parse(const Token &tok, std::list &checks) const { // Variable declaration.. if (tok.varId() && Token::Match(&tok, "%var% [[;]")) { const Variable* var2 = tok.variable(); if (var2 && var2->nameToken() == &tok && !var2->isStatic() && !var2->isExtern() && !var2->isConst() && !Token::simpleMatch(tok.linkAt(1), "] [")) { if (tok.linkAt(1)) { // array const Token* endtok = tok.next(); while (endtok->link()) endtok = endtok->link()->next(); if (endtok->str() != ";") return &tok; } const Scope* parent = var2->scope()->nestedIn; while (parent) { for (std::list::const_iterator j = parent->varlist.begin(); j != parent->varlist.end(); ++j) { if (j->name() == var2->name()) { ExecutionPath::bailOutVar(checks, j->declarationId()); // If there is a variable with the same name in other scopes, this might cause false positives, if there are unexpanded macros break; } } parent = parent->nestedIn; } if (var2->isPointer()) checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC)); else if (var2->typeEndToken()->str() != ">") { bool stdtype = false; // TODO: change to isC to handle unknown types better for (const Token* tok2 = var2->typeStartToken(); tok2 != var2->nameToken(); tok2 = tok2->next()) { if (tok2->isStandardType()) { stdtype = true; break; } } if (stdtype && (!var2->isArray() || var2->nameToken()->linkAt(1)->strAt(1) == ";")) checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC)); } return &tok; } } if (tok.str() == "return") { // is there assignment or ternary operator in the return statement? bool assignment = false; for (const Token *tok2 = tok.next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->str() == "=" || (!isC && tok2->str() == ">>") || Token::Match(tok2, "(|, &")) { assignment = true; break; } if (Token::Match(tok2, "[(,] &| %var% [,)]")) { tok2 = tok2->next(); if (!tok2->isName()) tok2 = tok2->next(); ExecutionPath::bailOutVar(checks, tok2->varId()); } } if (!assignment) { for (const Token *tok2 = tok.next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->isName() && tok2->strAt(1) == "(") tok2 = tok2->next()->link(); else if (tok2->varId()) use(checks, tok2); } } } if (tok.varId()) { // array variable passed as function parameter.. if (Token::Match(tok.previous(), "[(,] %var% [+-,)]")) { // #4896 : This checking was removed because of FP, // the new uninitvar checking is used instead to catch // these errors. ExecutionPath::bailOutVar(checks, tok.varId()); return &tok; } // Used.. if (Token::Match(tok.previous(), "[[(,+-*/|=] %var% ]|)|,|;|%op%") && !tok.next()->isAssignmentOp()) { // Taking address of array.. std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { UninitVar *c = dynamic_cast(*it); if (c && c->varId == tok.varId()) { if (c->var->isArray() || c->alloc) bailOutVar(checks, tok.varId()); break; } } // initialize reference variable if (Token::Match(tok.tokAt(-3), "& %var% =")) bailOutVar(checks, tok.varId()); else use(checks, &tok); return &tok; } if ((tok.previous() && tok.previous()->type() == Token::eIncDecOp) || (tok.next() && tok.next()->type() == Token::eIncDecOp)) { use(checks, &tok); return &tok; } if (Token::Match(tok.previous(), "[;{}] %var% [=[.]")) { if (tok.next()->str() == ".") { if (Token::Match(&tok, "%var% . %var% (")) { const Function *function = tok.tokAt(2)->function(); if (function && function->isStatic) return &tok; } if (use_dead_pointer(checks, &tok)) { return &tok; } } else { const Token *tok2 = tok.next(); if (tok2->str() == "[") { const Token *tok3 = tok2->link(); while (Token::simpleMatch(tok3, "] [")) tok3 = tok3->next()->link(); // Possible initialization if (Token::simpleMatch(tok3, "] >>")) return &tok; if (Token::simpleMatch(tok3, "] =")) { if (use_dead_pointer(checks, &tok)) { return &tok; } parserhs(tok2, checks); tok2 = tok3->next(); } } parserhs(tok2, checks); } // pointer aliasing? if (Token::Match(tok.tokAt(2), "%var% ;")) { pointer_assignment(checks, &tok, tok.tokAt(2)); } } if (tok.strAt(1) == "(") { use_pointer(checks, &tok); } if (Token::Match(tok.tokAt(-2), "[;{}] *")) { if (tok.strAt(1) == "=") { // is the pointer used in the rhs? bool used = false; for (const Token *tok2 = tok.tokAt(2); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[,;=(]")) break; else if (Token::Match(tok2, "* %varid%", tok.varId())) { used = true; break; } } if (used) use_pointer(checks, &tok); else init_pointer(checks, &tok); } else { use_pointer(checks, &tok); } return &tok; } if (Token::Match(tok.next(), "= malloc|kmalloc") || Token::simpleMatch(tok.next(), "= new char [") || (Token::Match(tok.next(), "= %var% (") && library->returnuninitdata.find(tok.strAt(2)) != library->returnuninitdata.end())) { alloc_pointer(checks, tok.varId()); if (tok.strAt(3) == "(") return tok.tokAt(3); } else if ((!isC && (Token::Match(tok.previous(), "<<|>>") || Token::Match(tok.previous(), "[;{}] %var% <<"))) || tok.strAt(1) == "=") { // TODO: Don't bail out for "<<" and ">>" if these are // just computations ExecutionPath::bailOutVar(checks, tok.varId()); return &tok; } if (tok.strAt(1) == "[" && tok.next()->link()) { const Token *tok2 = tok.next()->link(); if (tok2->strAt(1) == "=") { ExecutionPath::bailOutVar(checks, tok.varId()); return &tok; } } if (tok.strAt(-1) == "delete" || Token::simpleMatch(tok.tokAt(-3), "delete [ ]")) { dealloc_pointer(checks, &tok); return &tok; } } if (Token::Match(&tok, "%var% (") && uvarFunctions.find(tok.str()) == uvarFunctions.end()) { // sizeof/typeof doesn't dereference. A function name that is all uppercase // might be an unexpanded macro that uses sizeof/typeof if (Token::Match(&tok, "sizeof|typeof (")) return tok.next()->link(); // deallocate pointer if (Token::Match(&tok, "free|kfree|fclose ( %var% )") || Token::Match(&tok, "realloc ( %var%")) { dealloc_pointer(checks, tok.tokAt(2)); if (tok.str() == "realloc") ExecutionPath::bailOutVar(checks, tok.tokAt(2)->varId()); return tok.tokAt(3); } // parse usage.. { std::list var1; CheckNullPointer::parseFunctionCall(tok, var1, library, 1); for (std::list::const_iterator it = var1.begin(); it != var1.end(); ++it) { // does iterator point at first function parameter? const bool firstPar(*it == tok.tokAt(2)); // is function memset/memcpy/etc? if (tok.str().compare(0,3,"mem") == 0) use_array_mem(checks, *it); // second parameter for strncpy/strncat/etc else if (!firstPar && tok.str().compare(0,4,"strn") == 0) use_array_mem(checks, *it); else use_array(checks, *it); use_dead_pointer(checks, *it); } // Using uninitialized pointer is bad if using null pointer is bad std::list var2; CheckNullPointer::parseFunctionCall(tok, var2, library, 0); for (std::list::const_iterator it = var2.begin(); it != var2.end(); ++it) { if (std::find(var1.begin(), var1.end(), *it) == var1.end()) use_dead_pointer(checks, *it); } } // strncpy doesn't null-terminate first parameter if (Token::Match(&tok, "strncpy ( %var% ,")) { if (Token::Match(tok.tokAt(4), "%str% ,")) { if (Token::Match(tok.tokAt(6), "%num% )")) { const std::size_t len = Token::getStrLength(tok.tokAt(4)); const MathLib::bigint sz = MathLib::toLongNumber(tok.strAt(6)); if (sz >= 0 && len >= static_cast(sz)) { init_strncpy(checks, tok.tokAt(2)); return tok.next()->link(); } } } else { init_strncpy(checks, tok.tokAt(2)); return tok.next()->link(); } } // memset (not zero terminated).. if (Token::Match(&tok, "memset ( %var% , !!0 , %num% )")) { init_memset_nonzero(checks, tok.tokAt(2)); return tok.next()->link(); } if (Token::Match(&tok, "asm ( %str% )")) { ExecutionPath::bailOut(checks); return &tok; } // is the variable passed as a parameter to some function? unsigned int parlevel = 0; std::set bailouts; for (const Token *tok2 = tok.next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { if (parlevel <= 1) break; --parlevel; } else if (Token::Match(tok2, "sizeof|typeof (")) { tok2 = tok2->next()->link(); if (!tok2) break; } // ticket #2367 : unexpanded macro that uses sizeof|typeof? else if (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) { tok2 = tok2->next()->link(); if (!tok2) break; } else if (tok2->varId()) { if (Token::Match(tok2->tokAt(-2), "[(,] *") || Token::Match(tok2->next(), ". %var%")) { // find function call.. const Token *functionCall = tok2; while (nullptr != (functionCall = functionCall ? functionCall->previous() : 0)) { if (functionCall->str() == "(") break; if (functionCall->str() == ")") functionCall = functionCall->link(); } functionCall = functionCall ? functionCall->previous() : 0; if (functionCall) { if (functionCall->isName() && !functionCall->isUpperCaseName() && use_dead_pointer(checks, tok2)) ExecutionPath::bailOutVar(checks, tok2->varId()); } } // it is possible that the variable is initialized here if (Token::Match(tok2->previous(), "[(,] %var% [,)]")) bailouts.insert(tok2->varId()); // array initialization.. if (Token::Match(tok2->previous(), "[,(] %var% [+-]")) { // if var is array, bailout for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) { if ((*it)->varId == tok2->varId()) { const UninitVar *c = dynamic_cast(*it); if (c && (c->var->isArray() || (c->var->isPointer() && c->alloc))) bailouts.insert(tok2->varId()); break; } } } } } for (std::set::const_iterator it = bailouts.begin(); it != bailouts.end(); ++it) ExecutionPath::bailOutVar(checks, *it); } // function call via function pointer if (Token::Match(&tok, "( * %var% ) (") || (Token::Match(&tok, "( *| %var% .|::") && Token::Match(tok.link()->tokAt(-2), ".|:: %var% ) ("))) { // is the variable passed as a parameter to some function? const Token *tok2 = tok.link()->next(); for (const Token* const end2 = tok2->link(); tok2 != end2; tok2 = tok2->next()) { if (tok2->varId()) { // it is possible that the variable is initialized here ExecutionPath::bailOutVar(checks, tok2->varId()); } } } if (tok.str() == "return") { // Todo: if (!array && .. if (Token::Match(tok.next(), "%var% ;")) { use(checks, tok.next()); } else if (Token::Match(tok.next(), "%var% [")) { use_array_or_pointer_data(checks, tok.next()); } } if (tok.varId()) { if (tok.strAt(-1) == "=") { if (Token::Match(tok.tokAt(-3), "& %var% =")) { bailOutVar(checks, tok.varId()); return &tok; } if (!Token::Match(tok.tokAt(-3), ". %var% =")) { if (!Token::Match(tok.tokAt(-3), "[;{}] %var% =")) { use(checks, &tok); return &tok; } const unsigned int varid2 = tok.tokAt(-2)->varId(); if (varid2) { { use(checks, &tok); return &tok; } } } } if (tok.strAt(1) == ".") { bailOutVar(checks, tok.varId()); return &tok; } if (tok.strAt(1) == "[") { ExecutionPath::bailOutVar(checks, tok.varId()); return &tok; } if (Token::Match(tok.tokAt(-2), "[,(=] *")) { use_pointer(checks, &tok); return &tok; } if (tok.strAt(-1) == "&") { ExecutionPath::bailOutVar(checks, tok.varId()); } } // Parse "for" if (Token::Match(&tok, "[;{}] for (")) { // initialized variables std::set varid1; varid1.insert(0); // Parse token const Token *tok2; // parse setup for (tok2 = tok.tokAt(3); tok2 != tok.link(); tok2 = tok2->next()) { if (tok2->str() == ";") break; if (tok2->varId()) varid1.insert(tok2->varId()); } if (tok2 == tok.link()) return &tok; // parse condition if (Token::Match(tok2, "; %var% <|<=|>=|> %num% ;")) { // If the variable hasn't been initialized then call "use" if (varid1.find(tok2->next()->varId()) == varid1.end()) use(checks, tok2->next()); } // goto stepcode tok2 = tok2->next(); while (tok2 && tok2->str() != ";") tok2 = tok2->next(); // parse the stepcode if (Token::Match(tok2, "; ++|-- %var% ) {") || Token::Match(tok2, "; %var% ++|-- ) {")) { // get id of variable.. unsigned int varid = tok2->next()->varId(); if (!varid) varid = tok2->tokAt(2)->varId(); // Check that the variable hasn't been initialized and // that it isn't initialized in the body.. if (varid1.find(varid) == varid1.end()) { for (const Token *tok3 = tok2->tokAt(5); tok3 && tok3 != tok2->linkAt(4); tok3 = tok3->next()) { if (tok3->varId() == varid) { varid = 0; // variable is used.. maybe it's initialized. clear the variable id. break; } } // If the variable isn't initialized in the body call "use" if (varid != 0) { // goto variable tok2 = tok2->next(); if (!tok2->varId()) tok2 = tok2->next(); // call "use" use(checks, tok2); } } } } return &tok; } bool parseCondition(const Token &tok, std::list &checks) { if (tok.varId() && Token::Match(&tok, "%var% <|<=|==|!=|)")) use(checks, &tok); else if (Token::Match(&tok, "!| %var% [") && !Token::simpleMatch(skipBrackets(tok.next()), "=")) use_array_or_pointer_data(checks, tok.str() == "!" ? tok.next() : &tok); else if (Token::Match(&tok, "!| %var% (")) { const Token * const ftok = (tok.str() == "!") ? tok.next() : &tok; std::list var1; CheckNullPointer::parseFunctionCall(*ftok, var1, library, 1); for (std::list::const_iterator it = var1.begin(); it != var1.end(); ++it) { // is function memset/memcpy/etc? if (ftok->str().compare(0,3,"mem") == 0) use_array_mem(checks, *it); else use_array(checks, *it); } } else if (Token::Match(&tok, "! %var% )")) { use(checks, &tok); return false; } return ExecutionPath::parseCondition(tok, checks); } void parseLoopBody(const Token *tok, std::list &checks) const { while (tok) { if (tok->str() == "{" || tok->str() == "}" || tok->str() == "for") return; if (Token::simpleMatch(tok, "if (")) { // bail out all variables that are used in the condition const Token* const end2 = tok->linkAt(1); for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { if (tok2->varId()) ExecutionPath::bailOutVar(checks, tok2->varId()); } } const Token *next = parse(*tok, checks); tok = next->next(); } } public: /** Functions that don't handle uninitialized variables well */ static std::set uvarFunctions; static void analyseFunctions(const Token * const tokens, std::set &func) { for (const Token *tok = tokens; tok; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); continue; } if (tok->str() != "::" && Token::Match(tok->next(), "%var% ( %type%")) { if (!Token::Match(tok->linkAt(2), ") [{;]")) continue; const Token *tok2 = tok->tokAt(3); while (tok2 && tok2->str() != ")") { if (tok2->str() == ",") tok2 = tok2->next(); if (Token::Match(tok2, "%type% %var% ,|)") && tok2->isStandardType()) { tok2 = tok2->tokAt(2); continue; } if (tok2->isStandardType() && Token::Match(tok2, "%type% & %var% ,|)")) { const unsigned int varid(tok2->tokAt(2)->varId()); // flags for read/write bool r = false, w = false; // check how the variable is used in the function unsigned int indentlevel = 0; for (const Token *tok3 = tok2; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++indentlevel; else if (tok3->str() == "}") { if (indentlevel <= 1) break; --indentlevel; } else if (indentlevel == 0 && tok3->str() == ";") break; else if (indentlevel >= 1 && tok3->varId() == varid) { if (tok3->previous()->type() == Token::eIncDecOp || tok3->next()->type() == Token::eIncDecOp) { r = true; } else { w = true; break; } } } if (!r || w) break; tok2 = tok2->tokAt(3); continue; } if (Token::Match(tok2, "const %type% &|*| const| %var% ,|)") && tok2->next()->isStandardType()) { tok2 = tok2->tokAt(3); while (tok2->isName()) tok2 = tok2->next(); continue; } if (Token::Match(tok2, "const %type% %var% [ ] ,|)") && tok2->next()->isStandardType()) { tok2 = tok2->tokAt(5); continue; } /// @todo enable this code. if pointer is written in function then dead pointer is invalid but valid pointer is ok. /* if (Token::Match(tok2, "const| struct| %type% * %var% ,|)")) { while (tok2->isName()) tok2 = tok2->next(); tok2 = tok2->tokAt(2); continue; } */ break; } // found simple function.. if (tok2 && tok2->link() == tok->tokAt(2)) func.insert(tok->next()->str()); } } } }; /** Functions that don't handle uninitialized variables well */ std::set UninitVar::uvarFunctions; /// @} void CheckUninitVar::analyse(const Token * tokens, std::set &func) const { UninitVar::analyseFunctions(tokens, func); } void CheckUninitVar::saveAnalysisData(const std::set &data) const { UninitVar::uvarFunctions.insert(data.begin(), data.end()); } void CheckUninitVar::executionPaths() { // check if variable is accessed uninitialized.. { // no writing if multiple threads are used (TODO: thread safe analysis?) if (_settings->_jobs == 1) UninitVar::analyseFunctions(_tokenizer->tokens(), UninitVar::uvarFunctions); UninitVar c(this, _tokenizer->getSymbolDatabase(), &_settings->library, _tokenizer->isC()); checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c); } } void CheckUninitVar::check() { 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()) { checkScope(&*scope); } } } void CheckUninitVar::checkScope(const Scope* scope) { 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->isConst() || i->isArray() || i->isReference()) continue; // don't warn for try/catch exception variable { const Token *start = i->typeStartToken(); while (start && start->isName()) start = start->previous(); if (start && Token::simpleMatch(start->previous(), "catch (")) continue; } if (i->nameToken()->strAt(1) == "(") continue; bool stdtype = _tokenizer->isC(); const Token* tok = i->typeStartToken(); for (; tok && tok->str() != ";" && tok->str() != "<"; tok = tok->next()) { if (tok->isStandardType()) stdtype = true; } while (tok && tok->str() != ";") tok = tok->next(); if (!tok) continue; if (Token::Match(i->nameToken(), "%var% =")) { checkRhs(i->nameToken(), *i, false, ""); continue; } if (stdtype || i->isPointer()) { bool alloc = false; checkScopeForVariable(scope, tok, *i, nullptr, nullptr, &alloc, ""); } if (Token::Match(i->typeStartToken(), "struct %type% *| %var% ;")) checkStruct(scope, 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(), "struct| %type% * %var% [,)]")) { // 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% = %var% (", arg->declarationId()) && _settings->library.returnuninitdata.count(tok->strAt(3)) == 1U) { if (arg->typeStartToken()->str() == "struct") checkStruct(scope, tok, *arg); else if (arg->typeStartToken()->isStandardType()) { bool alloc = false; checkScopeForVariable(scope, tok->next(), *arg, nullptr, nullptr, &alloc, ""); } } } } } } } void CheckUninitVar::checkStruct(const Scope* scope, const Token *tok, const Variable &structvar) { const Token *typeToken = structvar.typeStartToken(); if (typeToken->str() == "struct") typeToken = typeToken->next(); const std::string structname(typeToken->str()); 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 == structname && scope2->numConstructors == 0U) { for (std::list::const_iterator it = scope2->varlist.begin(); it != scope2->varlist.end(); ++it) { const Variable &var = *it; if (!var.isArray()) { // 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) { bool alloc = false; const Token *tok2 = tok; if (tok->str() == "}") tok2 = tok2->next(); checkScopeForVariable(scope, tok2, structvar, nullptr, nullptr, &alloc, var.name()); } } } } } } static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map &variableValue, bool *alwaysTrue, bool *alwaysFalse) { assert(Token::simpleMatch(tok, "if (")); const Token *vartok = tok->tokAt(2); const bool NOT(vartok->str() == "!"); if (NOT) vartok = vartok->next(); while (Token::Match(vartok, "%var% . %var%")) vartok = vartok->tokAt(2); std::map::const_iterator it = variableValue.find(vartok->varId()); if (it == variableValue.end()) return; // always true if (Token::Match(vartok, "%var% %oror%|)")) { if (NOT) *alwaysTrue = bool(it->second == 0); else *alwaysTrue = bool(it->second != 0); } else if (Token::Match(vartok, "%var% == %num% %or%|)")) { *alwaysTrue = bool(it->second == MathLib::toLongNumber(vartok->strAt(2))); } else if (Token::Match(vartok, "%var% != %num% %or%|)")) { *alwaysTrue = bool(it->second != MathLib::toLongNumber(vartok->strAt(2))); } // always false if (Token::Match(vartok, "%var% &&|)")) { if (NOT) *alwaysFalse = bool(it->second != 0); else *alwaysFalse = bool(it->second == 0); } else if (Token::Match(vartok, "%var% == %num% &&|)")) { *alwaysFalse = bool(it->second != MathLib::toLongNumber(vartok->strAt(2))); } else if (Token::Match(vartok, "%var% != %num% &&|)")) { *alwaysFalse = bool(it->second == MathLib::toLongNumber(vartok->strAt(2))); } } bool CheckUninitVar::checkScopeForVariable(const Scope* scope, const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, bool * const alloc, const std::string &membervar) { const bool suppressErrors(possibleInit && *possibleInit); if (possibleInit) *possibleInit = false; unsigned int number_of_if = 0; if (var.declarationId() == 0U) return true; // variable values std::map variableValue; static const int NOT_ZERO = (1<<30); // special variable value 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 true; } break; } // Unconditional inner scope.. if (tok->str() == "{" && Token::Match(tok->previous(), "[;{}]")) { if (checkScopeForVariable(scope, tok->next(), var, possibleInit, nullptr, alloc, membervar)) return true; tok = tok->link(); continue; } // assignment with nonzero constant.. if (Token::Match(tok->previous(), "[;{}] %var% = - %var% ;") && tok->varId() > 0) variableValue[tok->varId()] = NOT_ZERO; // Inner scope.. if (Token::simpleMatch(tok, "if (")) { bool alwaysTrue = false; bool alwaysFalse = false; conditionAlwaysTrueOrFalse(tok, variableValue, &alwaysTrue, &alwaysFalse); // initialization / usage in condition.. if (!alwaysTrue && checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0), alloc && *alloc, membervar)) return true; // checking if a not-zero variable is zero => bail out unsigned int condVarId = 0, condVarValue = 0; if (Token::Match(tok, "if ( %var% )")) { std::map::const_iterator it = variableValue.find(tok->tokAt(2)->varId()); if (it != variableValue.end() && it->second == NOT_ZERO) return true; // this scope is not fully analysed => return true else { condVarId = tok->tokAt(2)->varId(); condVarValue = NOT_ZERO; } } // goto the { tok = tok->next()->link()->next(); if (!tok) break; if (tok->str() == "{") { bool possibleInitIf(number_of_if > 0 || suppressErrors); bool noreturnIf = false; const bool initif = !alwaysFalse && checkScopeForVariable(scope, tok->next(), var, &possibleInitIf, &noreturnIf, alloc, membervar); // 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 (_settings->debugwarnings) { 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.nameToken()->str() + "'. can't determine if this condition can be false when previous condition is false: " + condition); } return true; } if (alwaysTrue && 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, "[;{}.] %var% = - %var% ;")) varValueIf[tok2->next()->varId()] = NOT_ZERO; if (Token::Match(tok2, "[;{}.] %var% = %num% ;")) varValueIf[tok2->next()->varId()] = (int)MathLib::toLongNumber(tok2->strAt(3)); } } if (initif && condVarId > 0U) variableValue[condVarId] = condVarValue ^ NOT_ZERO; // 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(number_of_if > 0 || suppressErrors); bool noreturnElse = false; const bool initelse = !alwaysTrue && checkScopeForVariable(scope, tok->next(), var, &possibleInitElse, nullptr, alloc, membervar); std::map varValueElse; if (!alwaysTrue && !initelse && !noreturnElse) { for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}.] %var% = - %var% ;")) varValueElse[tok2->next()->varId()] = NOT_ZERO; if (Token::Match(tok2, "[;{}.] %var% = %num% ;")) varValueElse[tok2->next()->varId()] = (int)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) && !noreturnIf && !noreturnElse) { ++number_of_if; variableValue.insert(varValueIf.begin(), varValueIf.end()); variableValue.insert(varValueElse.begin(), varValueElse.end()); } } } } // = { .. } 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 (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.. if (Token::Match(tok, "for|while (") || Token::simpleMatch(tok, "do {")) { const bool forwhile = Token::Match(tok, "for|while ("); // is variable initialized in for-head (don't report errors yet)? if (forwhile && checkIfForWhileHead(tok->next(), var, true, false, alloc && *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 && *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 && *alloc, membervar); } // goto "}" tok = tok2->link(); // do-while => goto ")" if (!forwhile) { // Assert that the tokens are '} while (' if (!Token::simpleMatch(tok, "} while (")) { if (_settings->debugwarnings) 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; } } } // TODO: handle loops, try, etc if (Token::simpleMatch(tok, ") {") || Token::Match(tok, "%var% {")) { return true; } // bailout if there is assembler code if (Token::simpleMatch(tok, "asm (")) { return true; } if (Token::Match(tok, "return|break|continue|throw|goto")) { if (noreturn) *noreturn = true; while (tok && tok->str() != ";") { // variable is seen.. if (tok->varId() == var.declarationId()) { if (!membervar.empty()) { if (Token::Match(tok, "%var% . %var% ;|%cop%") && tok->strAt(2) == membervar) uninitStructMemberError(tok, tok->str() + "." + membervar); else return true; } // Use variable else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), alloc && *alloc, _tokenizer->isCPP())) { if (alloc && *alloc) uninitdataError(tok, tok->str()); else uninitvarError(tok, tok->str()); } 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() == "?") // TODO: False negatives when "?:" is used. // Fix the tokenizer and then remove this bailout. // The tokenizer should replace "return x?y:z;" with "if(x)return y;return z;" return true; tok = tok->next(); } return bool(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(), "= %var% (") && Token::simpleMatch(tok->linkAt(3), ") ;") && _settings->library.returnuninitdata.count(tok->strAt(2)) > 0U) { if (alloc) *alloc = true; continue; } if (!membervar.empty()) { if (isMemberVariableAssignment(tok, membervar)) { checkRhs(tok, var, alloc && *alloc, membervar); return true; } if (isMemberVariableUsage(tok, var.isPointer(), alloc && *alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); else if (Token::Match(tok->previous(), "[(,] %var% [,)]")) return true; } else { // Use variable if (!suppressErrors && isVariableUsage(tok, var.isPointer(), alloc && *alloc, _tokenizer->isCPP())) { if (alloc && *alloc) uninitdataError(tok, tok->str()); else uninitvarError(tok, tok->str()); } else { if (tok->strAt(1) == "=") checkRhs(tok, var, alloc && *alloc, ""); // assume that variable is assigned return true; } } } } return false; } bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, bool alloc, const std::string &membervar) { const Token * const endpar = startparentheses->link(); for (const Token *tok = startparentheses->next(); tok && tok != endpar; tok = tok->next()) { if (tok->varId() == var.declarationId()) { if (Token::Match(tok, "%var% . %var%")) { 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, _tokenizer->isCPP())) { if (!suppressErrors) uninitvarError(tok, tok->str()); else continue; } 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 bool 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 (tok->varId() == var.declarationId()) { if (!membervar.empty()) { if (isMemberVariableAssignment(tok, membervar)) { bool assign = true; bool rhs = false; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "=") rhs = true; if (tok2->str() == ";") break; if (rhs && tok2->varId() == var.declarationId() && isMemberVariableUsage(tok2, var.isPointer(), alloc, membervar)) { assign = false; break; } } if (assign) return true; } if (Token::Match(tok, "%var% =")) return true; if (isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) usetok = tok; else if (Token::Match(tok->previous(), "[(,] %var% [,)]")) return true; } else { if (isVariableUsage(tok, var.isPointer(), alloc, _tokenizer->isCPP())) 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 (Token::Match(tok, "sizeof|typeof (")) tok = tok->next()->link(); if (Token::Match(tok, "asm ( %str% ) ;")) return true; } if (!suppressErrors && usetok) { if (membervar.empty()) uninitvarError(usetok, usetok->str()); else uninitStructMemberError(usetok, usetok->str() + "." + membervar); return true; } return false; } void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, bool alloc, 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, _tokenizer->isCPP())) uninitvarError(tok, tok->str()); else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); } 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 (Token::simpleMatch(tok, "sizeof (")) tok = tok->next()->link(); } } bool CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, bool alloc, bool cpp) { if (vartok->previous()->str() == "return" && !alloc) return true; // Passing variable to typeof/__alignof__ if (Token::Match(vartok->tokAt(-3), "typeof|__alignof__ ( * %var%")) return false; // Passing variable to function.. if (Token::Match(vartok->previous(), "[(,] %var% [,)]") || Token::Match(vartok->tokAt(-2), "[(,] & %var% [,)]")) { const bool address(vartok->previous()->str() == "&"); // 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(); } // is this a function call? if (start && Token::Match(start->previous(), "%var% (")) { // 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(); while (argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); if (!address && Token::Match(argStart, "const| struct| %type% [,)]")) return true; if (!address && Token::Match(argStart, "const| struct| %type% %var% [,)]")) return true; if (Token::Match(argStart, "const %type% & %var% [,)]")) return true; if (pointer && !address && !alloc && Token::Match(argStart, "struct| %type% * %var% [,)]")) return true; if ((pointer || address) && !alloc && Token::Match(argStart, "const struct| %type% * %var% [,)]")) return true; if ((pointer || address) && Token::Match(argStart, "const %type% %var% [") && Token::Match(argStart->linkAt(3), "] [,)]")) return true; } } else if (Token::Match(start->previous(), "if|while|for")) { // control-flow statement reading the variable "by value" return !alloc; } } } if (Token::Match(vartok->previous(), "++|--|%cop%")) { if (cpp && vartok->previous()->str() == ">>") { // assume that variable is initialized return false; } // is there something like: ; "*((&var ..expr.. =" => the variable is assigned if (vartok->previous()->str() == "&") { const Token *tok2 = vartok->tokAt(-2); if (tok2 && (tok2->isConstOp() || Token::Match(tok2, "[;{}(=]"))) return false; // address of if (tok2 && tok2->str() == ")") tok2 = tok2->link()->previous(); if (Token::Match(tok2,"[()] ( %type% *| ) &") && tok2->tokAt(2)->varId() == 0) return false; // cast while (tok2 && tok2->str() == "(") tok2 = tok2->previous(); while (tok2 && tok2->str() == "*") tok2 = tok2->previous(); if (Token::Match(tok2, "[;{}] *")) { // there is some such code before vartok: "[*]+ [(]* &" // determine if there is a = after vartok for (tok2 = vartok; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}]")) break; if (tok2->str() == "=") return false; } } } if (vartok->previous()->str() != "&" || !Token::Match(vartok->tokAt(-2), "[(,=?:]")) { if (alloc && vartok->previous()->str() == "*") { const Token *parent = vartok->previous()->astParent(); if (parent && parent->str() == "=" && parent->astOperand1() == vartok->previous()) return false; return true; } return !alloc; } } if (!alloc && Token::Match(vartok->previous(), "= %var% ;|%cop%")) return true; if (Token::Match(vartok->previous(), "? %var%")) { // this is only variable usage if variable is either: // * unconditionally uninitialized // * used in both rhs and lhs of ':' operator bool rhs = false; for (const Token *tok2 = vartok; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ":") rhs = true; else if (Token::Match(tok2, "[)];,{}=]")) break; else if (rhs && tok2->varId() == vartok->varId()) return true; } } bool unknown = false; if (pointer && CheckNullPointer::isPointerDeRef(vartok, unknown)) { // pointer is allocated - dereferencing it is ok. if (alloc) return false; // function parameter? bool functionParameter = false; if (Token::Match(vartok->tokAt(-2), "%var% (") || vartok->previous()->str() == ",") functionParameter = true; // if this is not a function parameter report this dereference as variable usage if (!functionParameter) return true; } if (pointer && Token::Match(vartok, "%var% . %var% (")) { const Function *function = vartok->tokAt(2)->function(); return (!function || !function->isStatic); } if (cpp && Token::Match(vartok->next(), "<<|>>")) { // Is this calculation done in rhs? const Token *tok = vartok; while (tok && Token::Match(tok, "%var%|.|::")) 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()); } if (!alloc && vartok->next() && vartok->next()->isOp() && !vartok->next()->isAssignmentOp()) return true; if (vartok->strAt(1) == "]") return true; return false; } bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) { if (Token::Match(tok, "%var% . %var%") && tok->strAt(2) == membervar) { if (Token::Match(tok->tokAt(3), "[=.[]")) return true; else if (Token::Match(tok->tokAt(-2), "[(,=] &")) 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 return true; } else if (tok->strAt(1) == "=") return true; else if (tok->strAt(-1) == "&") { if (Token::Match(tok->tokAt(-2), "[(,] & %var%")) { // 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() : NULL; if (Token::Match(ftok, "%var% (")) { // check how function handle uninitialized data arguments.. const Function *function = ftok->function(); const Variable *arg = function ? function->getArgumentVar(argumentNumber) : NULL; const Token *argStart = arg ? arg->typeStartToken() : NULL; while (argStart && argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); if (Token::Match(argStart, "const struct| %type% * const| %var% [,)]")) return false; } else if (Token::simpleMatch(ftok ? ftok->previous() : nullptr, "= * (")) return false; } return true; } return false; } bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, bool alloc, const std::string &membervar) const { if (isMemberVariableAssignment(tok, membervar)) return false; if (Token::Match(tok, "%var% . %var%") && tok->strAt(2) == membervar) return true; else if (!isPointer && Token::Match(tok->previous(), "[(,] %var% [,)]") && isVariableUsage(tok, isPointer, alloc, _tokenizer->isCPP())) return true; else if (!isPointer && Token::Match(tok->previous(), "= %var% ;")) 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), "[(,] & %var% [,)]") && isVariableUsage(tok, isPointer, alloc, _tokenizer->isCPP())) 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).")); } void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "uninitdata", "Memory is allocated but not initialized: " + varname); } void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "uninitvar", "Uninitialized variable: " + varname); } void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) { reportError(tok, Severity::error, "uninitStructMember", "Uninitialized struct member: " + membername); } cppcheck-1.66/lib/checkuninitvar.h000066400000000000000000000111231236713773000171710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" class Scope; class Variable; /// @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()), testrunner(false) { } /** @brief This constructor is used when running checks. */ CheckUninitVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), testrunner(false) { } /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); checkUninitVar.executionPaths(); checkUninitVar.check(); } /** Check for uninitialized variables */ void check(); void checkScope(const Scope* scope); void checkStruct(const Scope* scope, const Token *tok, const Variable &structvar); bool checkScopeForVariable(const Scope* scope, const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, bool * const alloc, const std::string &membervar); bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, bool alloc, const std::string &membervar); bool checkLoopBody(const Token *tok, const Variable& var, const bool alloc, const std::string &membervar, const bool suppressErrors); void checkRhs(const Token *tok, const Variable &var, bool alloc, const std::string &membervar); static bool isVariableUsage(const Token *vartok, bool ispointer, bool alloc, bool cpp); static bool isMemberVariableAssignment(const Token *tok, const std::string &membervar); bool isMemberVariableUsage(const Token *tok, bool isPointer, bool alloc, const std::string &membervar) const; /** * @brief Uninitialized variables: analyse functions to see how they work with uninitialized variables * @param tokens [in] the token list * @param func [out] names of functions that don't handle uninitialized variables well. the function names are added to the set. No clearing is made. */ void analyse(const Token * tokens, std::set &func) const; /** Save analysis results */ void saveAnalysisData(const std::set &data) const; /** @brief new type of check: check execution paths */ void executionPaths(); 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 uninitStructMemberError(const Token *tok, const std::string &membername); /** testrunner: (don't abort() when assertion fails, just write error message) */ bool testrunner; private: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckUninitVar c(0, settings, errorLogger); // error c.uninitstringError(0, "varname", true); c.uninitdataError(0, "varname"); c.uninitvarError(0, "varname"); c.uninitStructMemberError(0, "a.b"); } static std::string myName() { return "Uninitialized variables"; } std::string classInfo() const { return "Uninitialized variables\n" "* using uninitialized variables and data\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkuninitvarH cppcheck-1.66/lib/checkunusedfunctions.cpp000066400000000000000000000250101236713773000207410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "token.h" #include "symboldatabase.h" #include //--------------------------------------------------------------------------- // Register this check class CheckUnusedFunctions CheckUnusedFunctions::instance; //--------------------------------------------------------------------------- // FUNCTION USAGE - Check for unused functions etc //--------------------------------------------------------------------------- void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings) { const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase(); // Function declarations.. 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) continue; // Don't care about templates if (func->retDef->str() == "template") continue; 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.. 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)); int scope = 0; bool start = true; // find all function calls in library code (starts with '(', not if or while etc) while (scope || start) { 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())) { 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 (!settings->library.markupFile(FileName) // only check source files && settings->library.isexporter(tok->str()) && tok->next() != 0) { const Token * qPropToken = tok; qPropToken = qPropToken->next(); while (qPropToken && qPropToken->str() != ")") { if (settings->library.isexportedprefix(tok->str(), qPropToken->str())) { const Token* qNextPropToken = qPropToken->next(); const std::string& value = qNextPropToken->str(); if (_functions.find(value) != _functions.end()) { _functions[value].usedOtherFile = true; } } if (settings->library.isexportedsuffix(tok->str(), qPropToken->str())) { const Token* qNextPropToken = qPropToken->previous(); const std::string& value = qNextPropToken->str(); if (value != ")" && _functions.find(value) != _functions.end()) { _functions[value].usedOtherFile = true; } } qPropToken = qPropToken->next(); } } if (settings->library.markupFile(FileName) && settings->library.isimporter(FileName, tok->str()) && tok->next()) { const Token * qPropToken = tok; qPropToken = qPropToken->next(); if (qPropToken->next()) { qPropToken = qPropToken->next(); while (qPropToken && qPropToken->str() != ")") { const std::string& value = qPropToken->str(); if (!value.empty()) { _functions[value].usedOtherFile = true; break; } qPropToken = qPropToken->next(); } } } if (settings->library.isreflection(tok->str())) { const int index = settings->library.reflectionArgument(tok->str()); if (index >= 0) { const Token * funcToken = tok->next(); int p = 0; std::string value; while (funcToken) { if (funcToken->str()==",") { if (++p==index) break; value = ""; } else value += funcToken->str(); funcToken = funcToken->next(); } if (p==index) { value = value.substr(1, value.length() - 2); _functions[value].usedOtherFile = true; } } } const Token *funcname = nullptr; if (tok->scope()->isExecutable() && Token::Match(tok->next(), "%var% (")) { funcname = tok->next(); } else if (tok->scope()->isExecutable() && Token::Match(tok->next(), "%var% <") && Token::simpleMatch(tok->linkAt(2), "> (")) { funcname = tok->next(); } else if (Token::Match(tok, "[;{}.,()[=+-/|!?:] &| %var% [(),;:}]")) { funcname = tok->next(); if (tok->str() == "&") funcname = funcname->next(); } else if (Token::Match(tok, "[;{}.,()[=+-/|!?:] &| %var% :: %var%")) { funcname = tok->next(); if (funcname->str() == "&") funcname = funcname->next(); while (Token::Match(funcname,"%var% :: %var%")) funcname = funcname->tokAt(2); if (!Token::Match(funcname, "%var% [(),;:}]")) continue; } else continue; // funcname ( => Assert that the end parentheses isn't followed by { if (Token::Match(funcname, "%var% (|<")) { 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()]; if (func.filename.empty() || func.filename == "+") func.usedOtherFile = true; else func.usedSameFile = true; } } } void CheckUnusedFunctions::check(ErrorLogger * const errorLogger) { 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" || it->first == "WinMain" || it->first == "_tmain" || it->first == "if" || (it->first.compare(0, 8, "operator") == 0 && it->first.size() > 8 && !std::isalnum(it->first[8]))) continue; if (! func.usedSameFile) { std::string filename; if (func.filename == "+") filename = ""; else filename = func.filename; unusedFunctionError(errorLogger, filename, func.lineNumber, it->first); } 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() ); */ } } } 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, Severity::style, "The function '" + funcname + "' is never used.", "unusedFunction", false); if (errorLogger) errorLogger->reportErr(errmsg); else reportError(errmsg); } cppcheck-1.66/lib/checkunusedfunctions.h000066400000000000000000000062001236713773000204060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" /// @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); void check(ErrorLogger * const errorLogger); static CheckUnusedFunctions instance; private: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckUnusedFunctions c(0, settings, errorLogger); c.unusedFunctionError(errorLogger, "", 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 *, const Settings *, 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; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedfunctionsH cppcheck-1.66/lib/checkunusedvar.cpp000066400000000000000000001427061236713773000175350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "symboldatabase.h" #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUnusedVar instance; } /** * @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: 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():0), _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 == false && _write == false); } 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: 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); // alias to self if (varid1 == varid2) { if (var1) var1->use(_varReadInScope); return; } if (replace) { // remove var1 from all aliases for (std::set::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::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::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::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::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::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) { usage->_modified = true; usage->_lastAccess = tok; std::set::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 0; } 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% !!;") && tok->varId() == tok->tokAt(2)->varId()) { return tok->tokAt(2); } 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->str() != "=") { 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, "(| &| %var%") || Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %var%")) { 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->str() == "&") { addressOf = true; tok = tok->next(); } } // no cast, no ? else if (!Token::Match(tok, "%var% ?")) { 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(); 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) 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), "%var% .")) { if (tok->tokAt(2)->varId()) { 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); } } } // Possible pointer alias else if (Token::Match(tok, "%var% = %var% ;")) { const unsigned int varid2 = tok->tokAt(2)->varId(); 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, ". %var%")) tok = tok->tokAt(2); else break; } return 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 = Variables::array; else if (i->isReference()) type = Variables::reference; else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*") type = Variables::pointerPointer; else if (i->isPointer()) type = Variables::pointer; else if (_tokenizer->isC() || i->typeEndToken()->isStandardType() || isRecordTypeWithoutSideEffects(i->type()) || (i->isStlType() && i->typeStartToken()->strAt(2) != "lock_guard" && i->typeStartToken()->strAt(2) != "unique_lock")) type = Variables::standard; if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken())) continue; const Token* defValTok = i->nameToken()->next(); for (; defValTok; defValTok = defValTok->next()) { if (defValTok->str() == "[") defValTok = defValTok->link(); else if (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(), "%var% [ %var% ]")) // Array index variable read. variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken()); if (defValTok && defValTok->str() == "=") { if (defValTok->next() && defValTok->next()->str() == "{") { for (const Token* tok = defValTok; tok && tok != defValTok->linkAt(1); tok = tok->next()) if (tok->varId()) // Variables used to initialize the array read. variables.read(tok->varId(), i->nameToken()); } else doAssignment(variables, i->nameToken(), false, scope); } else if (Token::Match(defValTok, "( %var% )")) // Variables used to initialize the variable read. variables.readAll(defValTok->next()->varId(), i->nameToken()); // ReadAll? } } // Check variable usage for (const Token *tok = scope->classDef->next(); 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() == "{") { 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::simpleMatch(tok, "goto")) { // https://sourceforge.net/apps/trac/cppcheck/ticket/4447 variables.clear(); break; } // bailout when for_each is used if (Token::Match(tok, "%var% (") && 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 (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()) { const Variable *var = tok2->variable(); if (var && var->nameToken() == tok2) { // Declaration: Skip tok = tok2->next(); if (Token::Match(tok, "( %var% )")) // 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 (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% )") || 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(3); } 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; } } else if (Token::Match(tok->tokAt(-2), "while|if") && tok->strAt(1) == "=" && tok->varId() && tok->varId() == tok->tokAt(2)->varId()) { variables.use(tok->tokAt(2)->varId(), tok); } // 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 (tok->type() == Token::eIncDecOp) { pre = true; tok = tok->next(); } if (tok->next()->type() == Token::eIncDecOp) post = true; const unsigned int varid1 = tok->varId(); const Token * const start = tok; tok = doAssignment(variables, tok, dereference, scope); 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 && 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, "%var% = 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 (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.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, "%var% .")) 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() == "=") { 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, "%var% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) || (Token::simpleMatch(tok, "* (") && Token::simpleMatch(tok->next()->link(), ") ="))) { if (tok->str() == "*") { tok = tok->tokAt(2); if (tok->str() == "(") tok = tok->link()->next(); } 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, false, 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, ">>|>>= %var%")) { 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, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) { variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write } // function else if (Token::Match(tok, "%var% (")) { variables.read(tok->varId(), tok); } else if (Token::Match(tok, "[{,] %var% [,}]")) { variables.read(tok->next()->varId(), tok); } else if (tok->varId() && Token::Match(tok, "%var% .")) { variables.use(tok->varId(), tok); // use = read + write } 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())) { 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()->type() == 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->next()->isAssignmentOp()) variables.write(tok2->varId(), tok); else variables.read(tok2->varId(), tok); } } } } } void CheckUnusedVar::checkFunctionVariableUsage() { if (!_settings->isEnabled("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]; // 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; const std::string &varname = usage._var->name(); const Variable* var = symbolDatabase->getVariableFromVarId(it->first); // variable has been marked as unused so ignore it if (usage._var->nameToken()->isAttributeUnused()) 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._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->isStlType()) unassignedVariableError(usage._var->nameToken(), varname); // variable has been written but not read else if (!usage._read && !usage._modified) unreadVariableError(usage._lastAccess, varname); // variable has been read but not written else if (!usage._write && !usage._allocateMemory && !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); } void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' is allocated memory that is never used."); } void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is assigned a value that is never used."); } void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unassignedVariable", "Variable '" + varname + "' is not assigned a value."); } //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- void CheckUnusedVar::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 = 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(); // Bail out if instance is initialized with {}.. if (!structname.empty()) { const std::string pattern1(structname + " %var% ;"); const Token *tok2 = tok; while (nullptr != (tok2 = Token::findmatch(tok2->next(), pattern1.c_str()))) { if (Token::simpleMatch(tok2->tokAt(3), (tok2->strAt(1) + " = {").c_str())) { structname.clear(); break; } } } // bail out for extern/global struct for (const Token *tok2 = Token::findmatch(tok, (structname + " %var%").c_str()); tok2 && tok2->next(); tok2 = Token::findmatch(tok2->next(), (structname + " %var%").c_str())) { const Variable *var = tok2->next()->variable(); if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic()))) { structname.clear(); break; } } if (structname.empty()) continue; // 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); } } } } void CheckUnusedVar::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."); } 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 */ 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 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.66/lib/checkunusedvar.h000066400000000000000000000101471236713773000171730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "check.h" #include class Type; class Scope; 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 checkVariableUsage(const Scope* const scope, const Token* start, Variables& variables); void checkFunctionVariableUsage(); /** @brief %Check that all struct members are used */ void checkStructMemberUsage(); private: bool isRecordTypeWithoutSideEffects(const Type* type); bool isEmptyType(const Type* type); // Error messages.. void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname); 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); void unassignedVariableError(const Token *tok, const std::string &varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckUnusedVar c(0, settings, errorLogger); // style/warning c.unusedVariableError(0, "varname"); c.allocatedButUnusedVariableError(0, "varname"); c.unreadVariableError(0, "varname"); c.unassignedVariableError(0, "varname"); c.unusedStructMemberError(0, "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.66/lib/config.h000066400000000000000000000007611236713773000154270ustar00rootroot00000000000000#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.66/lib/cppcheck.cpp000066400000000000000000000550031236713773000162740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "preprocessor.h" // Preprocessor #include "tokenize.h" // Tokenizer #include "checkunusedfunctions.h" #include "check.h" #include "path.h" #include #include #include #include #include "timer.h" #include "version.h" #ifdef HAVE_RULES #define PCRE_STATIC #include #endif static const char Version[] = CPPCHECK_VERSION_STRING; static const char ExtraVersion[] = ""; static TimerResults S_timerResults; CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions) : _errorLogger(errorLogger), exitcode(0), _useGlobalSuppressions(useGlobalSuppressions), tooManyConfigs(false), _simplify(true) { } CppCheck::~CppCheck() { 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) { return processFile(path, ""); } unsigned int CppCheck::check(const std::string &path, const std::string &content) { return processFile(path, content); } void CppCheck::replaceAll(std::string& code, const std::string &from, const std::string &to) { std::size_t pos = 0; while ((pos = code.find(from, pos)) != std::string::npos) { code.replace(pos, from.length(), to); pos += to.length(); } } bool CppCheck::findError(std::string code, const char FileName[]) { // First make sure that error occurs with the original code checkFile(code, FileName); if (_errorList.empty()) { // Error does not occur with this code return false; } std::string previousCode = code; std::string error = _errorList.front(); for (;;) { // Try to remove included files from the source std::size_t found = previousCode.rfind("\n#endfile"); if (found == std::string::npos) { // No modifications can be done to the code } else { // Modify code and re-check it to see if error // is still there. code = previousCode.substr(found+9); _errorList.clear(); checkFile(code, FileName); } if (_errorList.empty()) { // Latest code didn't fail anymore. Fall back // to previous code code = previousCode; } else { error = _errorList.front(); } // Add '\n' so that "\n#file" on first line would be found code = "// " + error + "\n" + code; replaceAll(code, "\n#file", "\n// #file"); replaceAll(code, "\n#endfile", "\n// #endfile"); // We have reduced the code as much as we can. Print out // the code and quit. _errorLogger.reportOut(code); break; } return true; } unsigned int CppCheck::processFile(const std::string& filename, const std::string& fileContent) { 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._errorsOnly == false) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); _errorLogger.reportOut(std::string("Checking ") + fixedpath + std::string("...")); } try { Preprocessor preprocessor(&_settings, this); std::list configurations; std::string filedata = ""; if (!fileContent.empty()) { // File content was given as a string (democlient) std::istringstream iss(fileContent); preprocessor.preprocess(iss, filedata, configurations, filename, _settings._includePaths); } else { // Only file name was given, read the content from file std::ifstream fin(filename.c_str()); Timer t("Preprocessor::preprocess", _settings._showtime, &S_timerResults); preprocessor.preprocess(fin, filedata, configurations, filename, _settings._includePaths); } // Unhandled chars found during preprocessing => abort checking this file if (preprocessor.foundUnhandledChars()) return 0; if (_settings.checkConfiguration) { return 0; } // Run rules on this code for (std::list::const_iterator it = _settings.rules.begin(); it != _settings.rules.end(); ++it) { if (it->tokenlist == "define") { Tokenizer tokenizer2(&_settings, this); std::istringstream istr2(filedata); tokenizer2.list.createTokens(istr2, filename); for (const Token *tok = tokenizer2.list.front(); tok; tok = tok->next()) { if (tok->str() == "#define") { std::string code = std::string(tok->linenr()-1U, '\n'); for (const Token *tok2 = tok; tok2 && tok2->linenr() == tok->linenr(); tok2 = tok2->next()) code += " " + tok2->str(); Tokenizer tokenizer3(&_settings, this); std::istringstream istr3(code); tokenizer3.list.createTokens(istr3, tokenizer2.list.file(tok)); executeRules("define", tokenizer3); } } break; } } if (!_settings.userDefines.empty() && _settings._maxConfigs==1U) { configurations.clear(); configurations.push_back(_settings.userDefines); } if (!_settings._force && configurations.size() > _settings._maxConfigs) { if (_settings.isEnabled("information")) { tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size()); } else { tooManyConfigs = true; } } unsigned int checkCount = 0; for (std::list::const_iterator it = configurations.begin(); it != configurations.end(); ++it) { // 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._errorsOnly == false && it != configurations.begin()) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); _errorLogger.reportOut(std::string("Checking ") + fixedpath + ": " + cfg + std::string("...")); } if (!_settings.userDefines.empty()) { if (!cfg.empty()) cfg = ";" + cfg; cfg = _settings.userDefines + cfg; } Timer t("Preprocessor::getcode", _settings._showtime, &S_timerResults); const std::string codeWithoutCfg = preprocessor.getcode(filedata, cfg, filename); t.Stop(); const std::string &appendCode = _settings.append(); if (_settings.debugFalsePositive) { if (findError(codeWithoutCfg + appendCode, filename.c_str())) { return exitcode; } } else { checkFile(codeWithoutCfg + appendCode, filename.c_str()); } } } catch (const std::runtime_error &e) { internalError(filename, e.what()); } catch (const InternalError &e) { internalError(filename, e.errorMessage); } if (_settings.isEnabled("information") || _settings.checkConfiguration) reportUnmatchedSuppressions(_settings.nomsg.getUnmatchedLocalSuppressions(filename)); _errorList.clear(); 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 a internal error: " + msg); if (_settings.isEnabled("information")) { const ErrorLogger::ErrorMessage::FileLocation loc1(filename, 0); std::list callstack; callstack.push_back(loc1); ErrorLogger::ErrorMessage errmsg(callstack, Severity::information, fullmsg, "internalError", false); _errorLogger.reportErr(errmsg); } else { // Report on stdout _errorLogger.reportOut(fullmsg); } } void CppCheck::checkFunctionUsage() { // This generates false positives - especially for libraries if (_settings.isEnabled("unusedFunction") && _settings._jobs == 1) { const bool verbose_orig = _settings._verbose; _settings._verbose = false; if (_settings._errorsOnly == false) _errorLogger.reportOut("Checking usage of global functions.."); CheckUnusedFunctions::instance.check(this); _settings._verbose = verbose_orig; } } void CppCheck::analyseFile(std::istream &fin, const std::string &filename) { // Preprocess file.. Preprocessor preprocessor(&_settings, this); std::list configurations; std::string filedata = ""; preprocessor.preprocess(fin, filedata, configurations, filename, _settings._includePaths); const std::string code = preprocessor.getcode(filedata, "", filename); if (_settings.checkConfiguration) { return; } // Tokenize.. Tokenizer tokenizer(&_settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename.c_str(), ""); tokenizer.simplifyTokenList2(); // Analyse the tokens.. std::set data; for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { (*it)->analyse(tokenizer.tokens(), data); } // Save analysis results.. // TODO: This loop should be protected by a mutex or something like that // The saveAnalysisData must _not_ be called from many threads at the same time. for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { (*it)->saveAnalysisData(data); } } //--------------------------------------------------------------------------- // CppCheck - A function that checks a specified file //--------------------------------------------------------------------------- void CppCheck::checkFile(const std::string &code, const char FileName[]) { if (_settings.terminated() || _settings.checkConfiguration) return; Tokenizer _tokenizer(&_settings, this); if (_settings._showtime != SHOWTIME_NONE) _tokenizer.setTimerResults(&S_timerResults); try { bool result; // Execute rules for "raw" code for (std::list::const_iterator it = _settings.rules.begin(); it != _settings.rules.end(); ++it) { if (it->tokenlist == "raw") { Tokenizer tokenizer2(&_settings, this); std::istringstream istr(code); tokenizer2.list.createTokens(istr, FileName); executeRules("raw", tokenizer2); break; } } // Tokenize the file std::istringstream istr(code); Timer timer("Tokenizer::tokenize", _settings._showtime, &S_timerResults); result = _tokenizer.tokenize(istr, FileName, cfg); timer.Stop(); if (!result) { // File had syntax errors, abort return; } // dump if (_settings.dump) { std::string dumpfile = std::string(FileName) + ".dump"; std::ofstream fdump(dumpfile.c_str()); if (fdump.is_open()) { fdump << "" << std::endl; fdump << "" << std::endl; _tokenizer.dump(fdump); fdump << "" << std::endl; } return; } // 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; Timer timerRunChecks((*it)->name() + "::runChecks", _settings._showtime, &S_timerResults); (*it)->runChecks(&_tokenizer, &_settings, this); } if (_settings.isEnabled("unusedFunction") && _settings._jobs == 1) CheckUnusedFunctions::instance.parseTokens(_tokenizer, FileName, &_settings); executeRules("normal", _tokenizer); if (!_simplify) return; Timer timer3("Tokenizer::simplifyTokenList2", _settings._showtime, &S_timerResults); result = _tokenizer.simplifyTokenList2(); timer3.Stop(); if (!result) return; // 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; Timer timerSimpleChecks((*it)->name() + "::runSimplifiedChecks", _settings._showtime, &S_timerResults); (*it)->runSimplifiedChecks(&_tokenizer, &_settings, this); } if (_settings.terminated()) return; executeRules("simple", _tokenizer); if (_settings.terminated()) return; } catch (const InternalError &e) { 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); const ErrorLogger::ErrorMessage errmsg(locationList, Severity::error, e.errorMessage, e.id, false); _errorLogger.reportErr(errmsg); } } 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.empty() || 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(), Severity::error, error, "pcre_compile", false); reportErr(errmsg); } continue; } int pos = 0; int ovector[30]; while (pos < (int)str.size() && 0 <= pcre_exec(re, nullptr, str.c_str(), (int)str.size(), pos, 0, ovector, 30)) { unsigned int pos1 = (unsigned int)ovector[0]; 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, Severity::fromString(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("information") && !tooManyConfigs) return; tooManyConfigs = false; if (_settings.isEnabled("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, Severity::information, msg.str(), "toomanyconfigs", false); reportErr(errmsg); } //--------------------------------------------------------------------------- void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg) { if (!_settings.library.reportErrors(msg.file0)) return; 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; if (_settings.debugFalsePositive) { // Don't print out error _errorList.push_back(errmsg); 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)) exitcode = 1; _errorList.push_back(errmsg); _errorLogger.reportErr(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() { 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, &_settings); Tokenizer::getErrorMessages(this, &_settings); Preprocessor::getErrorMessages(this, &_settings); } cppcheck-1.66/lib/cppcheck.h000066400000000000000000000147431236713773000157470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "config.h" #include "settings.h" #include "errorlogger.h" #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); /** * @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 Check function usage. * @note Call this after all files has been checked */ void checkFunctionUsage(); /** * @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(); /** * @brief Analyse file - It's public so unit tests can be written */ void analyseFile(std::istream &f, const std::string &filename); void tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations); void dontSimplify() { _simplify = false; } private: /** @brief There has been a internal error => Report information message */ void internalError(const std::string &filename, const std::string &msg); /** * @brief Process one file. * @param filename file name * @param fileContent If this is non-empty then the file will not be loaded * @return amount of errors found */ unsigned int processFile(const std::string& filename, const std::string& fileContent); /** @brief Check file */ void checkFile(const std::string &code, const char FileName[]); /** * @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); /** * @brief Check given code. If error is found, return true * and print out source of the file. Try to reduce the code * while still showing the error. */ bool findError(std::string code, const char FileName[]); /** * @brief Replace "from" strings with "to" strings in "code" * and return it. */ static void replaceAll(std::string& code, const std::string &from, const std::string &to); 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; }; /// @} //--------------------------------------------------------------------------- #endif // cppcheckH cppcheck-1.66/lib/cppcheck.vcxproj000066400000000000000000000635111236713773000172100ustar00rootroot00000000000000 Debug-PCRE Win32 Debug-PCRE x64 Debug Win32 Debug x64 Release-PCRE Win32 Release-PCRE x64 Release Win32 Release x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A} cppcheck_lib DynamicLibrary Unicode false v120_xp DynamicLibrary Unicode false v120_xp DynamicLibrary Unicode false v120 DynamicLibrary Unicode false v120 DynamicLibrary Unicode false v120_xp DynamicLibrary Unicode false v120_xp DynamicLibrary Unicode false v120 DynamicLibrary Unicode false v120 ..\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;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 MultiThreadedDebugDLL ../externals;%(AdditionalLibraryDirectories) true true false xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 MultiThreadedDebugDLL pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 MultiThreadedDebugDLL ../externals;%(AdditionalLibraryDirectories) true true xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 MultiThreadedDebugDLL pcre.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\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL true ../externals;%(AdditionalLibraryDirectories) false true true true xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDLL true pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false true true true xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true ../externals;%(AdditionalLibraryDirectories) false true true true xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4251;4512 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false true true true xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y cppcheck-1.66/lib/cppcheck.vcxproj.filters000066400000000000000000000214201236713773000206500ustar00rootroot00000000000000 {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 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.66/lib/cxx11emu.h000066400000000000000000000044341236713773000156360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 __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.66/lib/errorlogger.cpp000066400000000000000000000331551236713773000170510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "path.h" #include "cppcheck.h" #include "tokenlist.h" #include "token.h" #include #include #include #include #include InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) : token(tok), errorMessage(errorMsg) { switch (type) { case SYNTAX: id = "syntaxError"; break; case INTERNAL: id = "cppcheckError"; break; } } ErrorLogger::ErrorMessage::ErrorMessage() : _severity(Severity::none), _inconclusive(false) { } ErrorLogger::ErrorMessage::ErrorMessage(const std::list &callStack, 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 _severity(severity), // severity for this error message _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), _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); } 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(!(msg[msg.size()-1]=='\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); if (_inconclusive) { const std::string inconclusive("inconclusive"); oss << inconclusive.length() << " " << inconclusive; } oss << _shortMessage.length() << " " << _shortMessage; oss << _verboseMessage.length() << " " << _verboseMessage; oss << _callStack.size() << " "; for (std::list::const_iterator tok = _callStack.begin(); tok != _callStack.end(); ++tok) { std::ostringstream smallStream; smallStream << (*tok).line << ":" << (*tok).getfile(); 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::vector results; 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.push_back(temp); if (results.size() == 4) break; } _id = results[0]; _severity = Severity::fromString(results[1]); _shortMessage = results[2]; _verboseMessage = results[3]; 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) { char c = static_cast(iss.get()); temp.append(1, c); } ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(temp.substr(temp.find(':') + 1)); temp = temp.substr(0, temp.find(':')); std::istringstream fiss(temp); fiss >> loc.line; _callStack.push_back(loc); if (_callStack.size() >= stackSize) break; } return true; } std::string ErrorLogger::ErrorMessage::getXMLHeader(int xml_version) { // 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); // version 2 header if (xml_version == 2) { printer.PushAttribute("version", xml_version); 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(int xml_version) { return (xml_version<=1) ? "" : " \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" static std::string 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. es << '\\' << std::setbase(8) << std::setw(3) << std::setfill('0') << (unsigned)(unsigned char)*from; result += es.str(); } ++from; } return result; } std::string ErrorLogger::ErrorMessage::toXML(bool verbose, int version) const { // The default xml format if (version == 1) { // No inconclusive messages in the xml version 1 if (_inconclusive) return ""; tinyxml2::XMLPrinter printer(0, false, 1); printer.OpenElement("error", false); if (!_callStack.empty()) { printer.PushAttribute("file", _callStack.back().getfile().c_str()); printer.PushAttribute("line", _callStack.back().line); } printer.PushAttribute("id", _id.c_str()); printer.PushAttribute("severity", (_severity == Severity::error ? "error" : "style")); printer.PushAttribute("msg", (verbose ? _verboseMessage : _shortMessage).c_str()); printer.CloseElement(false); return printer.CStr(); } // The xml format you get when you use --xml-version=2 else if (version == 2) { tinyxml2::XMLPrinter printer(0, false, 2); printer.OpenElement("error", false); printer.PushAttribute("id", _id.c_str()); printer.PushAttribute("severity", Severity::toString(_severity).c_str()); printer.PushAttribute("msg", _shortMessage.c_str()); printer.PushAttribute("verbose", fixInvalidChars(_verboseMessage).c_str()); if (_inconclusive) printer.PushAttribute("inconclusive", "true"); for (std::list::const_reverse_iterator it = _callStack.rbegin(); it != _callStack.rend(); ++it) { printer.OpenElement("location", false); printer.PushAttribute("file", (*it).getfile().c_str()); printer.PushAttribute("line", (*it).line); printer.CloseElement(false); } printer.CloseElement(false); return printer.CStr(); } return ""; } 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() - searchFor.length() + 1; } } std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string &outputFormat) const { // Save this ErrorMessage in plain text. // No template is given if (outputFormat.length() == 0) { 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(); } // 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() ? "" : 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}", ""); findAndReplace(result, "{line}", ""); } 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; } } if (suppressed) continue; std::list callStack; callStack.push_back(ErrorLogger::ErrorMessage::FileLocation(i->file, i->line)); reportErr(ErrorLogger::ErrorMessage(callStack, 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* list) : line(tok->linenr()), _file(list->file(tok)) { } 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(); } cppcheck-1.66/lib/errorlogger.h000066400000000000000000000235261236713773000165170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include "config.h" #include "suppressions.h" class Token; class TokenList; /// @addtogroup Core /// @{ /** @brief Simple container to be thrown when internal error is detected. */ struct InternalError { enum Type {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(NULL, "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; } }; /** * @brief This is an interface, which the class responsible of error logging * should implement. */ class CPPCHECKLIB ErrorLogger { 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() : line(0) { } FileLocation(const std::string &file, unsigned int aline) : line(aline), _file(file) { } FileLocation(const Token* tok, const TokenList* list); /** * 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 line; private: std::string _file; }; ErrorMessage(const std::list &callStack, Severity::SeverityType severity, const std::string &msg, const std::string &id, bool inconclusive); ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive); ErrorMessage(); /** * Format the error message in XML format * @param verbose use verbose message * @param ver XML version */ std::string toXML(bool verbose, int ver) const; static std::string getXMLHeader(int xml_version); static std::string getXMLFooter(int xml_version); /** * 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; 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); /** Short message */ std::string _shortMessage; /** Verbose message */ std::string _verboseMessage; }; ErrorLogger() { } virtual ~ErrorLogger() { } /** * 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); }; /// @} //--------------------------------------------------------------------------- #endif // errorloggerH cppcheck-1.66/lib/executionpath.cpp000066400000000000000000000403261236713773000173760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "executionpath.h" #include "token.h" #include "symboldatabase.h" #include #include #include #include // default : bail out if the condition is has variable handling bool ExecutionPath::parseCondition(const Token &tok, std::list & checks) { unsigned int parlevel = 0; for (const Token *tok2 = &tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { if (parlevel == 0) break; --parlevel; } else if (Token::Match(tok2, "[;{}]")) break; if (tok2->varId() != 0) { bailOutVar(checks, tok2->varId()); } } for (std::list::iterator it = checks.begin(); it != checks.end();) { if ((*it)->varId > 0 && (*it)->numberOfIf >= 1) { delete *it; checks.erase(it++); } else { ++it; } } return false; } void ExecutionPath::print() const { std::cout << " varId=" << varId << " numberOfIf=" << numberOfIf << "\n"; } // I use this function when debugging ExecutionPaths with GDB /* static void printchecks(const std::list &checks) { for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) (*it)->print(); } */ /** * @brief Parse If/Switch body recursively. * @param tok First token in body. * @param checks The current checks * @param newchecks new checks * @param countif The countif set - count number of if for each execution path */ static void parseIfSwitchBody(const Token * const tok, const std::list &checks, std::list &newchecks, std::set &countif) { std::set countif2; std::list c; if (!checks.empty()) { std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { if ((*it)->numberOfIf == 0) c.push_back((*it)->copy()); if ((*it)->varId != 0) countif2.insert((*it)->varId); } } ExecutionPath::checkScope(tok, c); while (!c.empty()) { if (c.back()->varId == 0) { delete c.back(); c.pop_back(); continue; } bool duplicate = false; std::list::const_iterator it; for (it = checks.begin(); it != checks.end(); ++it) { if (*(*it) == *c.back() && (*it)->numberOfIf == c.back()->numberOfIf) { duplicate = true; countif2.erase((*it)->varId); break; } } if (!duplicate) newchecks.push_back(c.back()); else delete c.back(); c.pop_back(); } // Add countif2 ids to countif.. countif. countif.insert(countif2.begin(), countif2.end()); } void ExecutionPath::checkScope(const Token *tok, std::list &checks) { if (!tok || tok->str() == "}" || checks.empty()) return; const std::auto_ptr check(checks.front()->copy()); for (; tok; tok = tok->next()) { // might be a noreturn function.. if (Token::simpleMatch(tok->tokAt(-2), ") ; }") && Token::Match(tok->linkAt(-2)->tokAt(-2), "[;{}] %var% (") && tok->linkAt(-2)->previous()->varId() == 0) { ExecutionPath::bailOut(checks); return; } if (Token::simpleMatch(tok, "union {")) { tok = tok->next()->link(); continue; } if (tok->str() == "}") return; if (tok->str() == "break") { ExecutionPath::bailOut(checks); return; } if (Token::simpleMatch(tok, "while (")) { // parse condition if (checks.size() > 10 || check->parseCondition(*tok->tokAt(2), checks)) { ExecutionPath::bailOut(checks); return; } // skip "while (fgets()!=NULL)" if (Token::simpleMatch(tok, "while ( fgets (")) { const Token *tok2 = tok->linkAt(3); if (Token::simpleMatch(tok2, ") ) {")) { tok = tok2->linkAt(2); if (!tok) break; continue; } } } // goto/setjmp/longjmp => bailout else if (Token::Match(tok, "goto|setjmp|longjmp")) { ExecutionPath::bailOut(checks); return; } // ?: => bailout if (tok->str() == "?") { for (const Token *tok2 = tok; tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId() > 0) ExecutionPath::bailOutVar(checks, tok2->varId()); } } // for/while/switch/do .. bail out else if (Token::Match(tok, "for|while|switch|do")) { // goto { const Token *tok2 = tok->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link(); if (tok2 && tok2->str() == ")") tok2 = tok2->next(); if (!tok2 || tok2->str() != "{") { ExecutionPath::bailOut(checks); return; } if (tok->str() == "switch") { // parse condition if (checks.size() > 10 || check->parseCondition(*tok->next(), checks)) { ExecutionPath::bailOut(checks); return; } // what variable ids should the if be counted for? std::set countif; std::list newchecks; for (const Token* tok3 = tok2->next(); tok3; tok3 = tok3->next()) { if (tok3->str() == "{") tok3 = tok3->link(); else if (tok3->str() == "}") break; else if (tok3->str() == "case" && !Token::Match(tok3, "case %num% : ; case")) { parseIfSwitchBody(tok3, checks, newchecks, countif); } } // Add newchecks to checks.. std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks)); // Increase numberOfIf std::list::iterator it; for (it = checks.begin(); it != checks.end(); ++it) { if (countif.find((*it)->varId) != countif.end()) (*it)->numberOfIf++; } } // no switch else { for (const Token *tok3 = tok; tok3 && tok3 != tok2; tok3 = tok3->next()) { if (tok3->varId()) ExecutionPath::bailOutVar(checks, tok3->varId()); } // it is not certain that a for/while will be executed: for (std::list::iterator it = checks.begin(); it != checks.end();) { if ((*it)->numberOfIf > 0) { delete *it; checks.erase(it++); } else ++it; } // #2231 - loop body only contains a conditional initialization.. if (Token::simpleMatch(tok2->next(), "if (")) { // Start { for the if block const Token *tok3 = tok2->linkAt(2); if (Token::simpleMatch(tok3,") {")) { tok3 = tok3->next(); // End } for the if block const Token *tok4 = tok3->link(); if (Token::Match(tok3, "{ %var% =") && Token::simpleMatch(tok4, "} }") && Token::simpleMatch(tok4->tokAt(-2), "break ;")) { // Is there a assignment and then a break? const Token *t = Token::findsimplematch(tok3, ";"); if (t && t->tokAt(3) == tok4) { for (std::list::iterator it = checks.begin(); it != checks.end(); ++it) { if ((*it)->varId == tok3->next()->varId()) { (*it)->numberOfIf++; break; } } tok = tok2->link(); continue; } } } } // parse loop bodies check->parseLoopBody(tok2->next(), checks); } // skip { .. } tok2 = tok2->link(); // if "do { .. } while ( .." , goto end of while.. if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok2, "} while (")) tok2 = tok2->linkAt(2); // bail out all variables if the scope contains a "return" // bail out all variables used in this for/while/switch/do for (; tok && tok != tok2; tok = tok->next()) { if (tok->str() == "return") ExecutionPath::bailOut(checks); if (tok->varId()) ExecutionPath::bailOutVar(checks, tok->varId()); } continue; } // bailout used variables in '; FOREACH ( .. ) { .. }' else if (tok->str() != "if" && Token::Match(tok->previous(), "[;{}] %var% (")) { // goto { const Token *tok2 = tok->next()->link()->next(); if (tok2 && tok2->str() == "{") { // goto "}" tok2 = tok2->link(); // bail out all variables used in "{ .. }" for (; tok && tok != tok2; tok = tok->next()) { if (tok->varId()) ExecutionPath::bailOutVar(checks, tok->varId()); } } } // .. ) { ... } => bail out if (tok->str() == ")" && tok->next() && tok->next()->str() == "{") { ExecutionPath::bailOut(checks); return; } if ((tok->str() == "abort" || tok->str() == "exit") && tok->next() && tok->next()->str() == "(") { ExecutionPath::bailOut(checks); return; } // don't parse into "struct type { .." if (Token::Match(tok, "struct|union|class %type% {|:")) { while (tok && tok->str() != "{" && tok->str() != ";") tok = tok->next(); tok = tok ? tok->link() : 0; if (!tok) { ExecutionPath::bailOut(checks); return; } } if (Token::simpleMatch(tok, "= {")) { // GCC struct initialization.. bail out if (Token::Match(tok->tokAt(2), ". %var% =")) { ExecutionPath::bailOut(checks); return; } const Token * const end = tok->next()->link(); while (tok && tok != end) { if (Token::Match(tok, "[{,] & %var% [,}]")) ExecutionPath::bailOutVar(checks, tok->tokAt(2)->varId()); tok = tok->next(); } if (!tok) { ExecutionPath::bailOut(checks); return; } continue; } // ; { ... } if (tok && Token::Match(tok->previous(), "[;{}:] {")) { ExecutionPath::checkScope(tok->next(), checks); tok = tok->link(); continue; } if (tok && tok->str() == "if" && tok->next() && tok->next()->str() == "(") { // what variable ids should the numberOfIf be counted for? std::set countif; std::list newchecks; while (tok->str() == "if" && tok->next() && tok->next()->str() == "(") { // goto "(" tok = tok->next(); // parse condition if (checks.size() > 10 || check->parseCondition(*tok->next(), checks)) { ExecutionPath::bailOut(checks); ExecutionPath::bailOut(newchecks); return; } // goto ")" tok = tok->link(); // goto "{" tok = tok->next(); if (!tok || tok->str() != "{") { ExecutionPath::bailOut(checks); ExecutionPath::bailOut(newchecks); return; } // Recursively check into the if .. parseIfSwitchBody(tok->next(), checks, newchecks, countif); // goto "}" tok = tok->link(); // there is no else => break out if (!tok->next() || tok->next()->str() != "else") break; // parse next "if".. tok = tok->tokAt(2); if (tok && tok->str() == "if") continue; if (!tok) { ExecutionPath::bailOut(newchecks); return; } // there is no "if".. ExecutionPath::checkScope(tok->next(), checks); tok = tok->link(); if (!tok) { ExecutionPath::bailOut(newchecks); return; } } // Add newchecks to checks.. std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks)); // Increase numberOfIf std::list::iterator it; for (it = checks.begin(); it != checks.end(); ++it) { if (countif.find((*it)->varId) != countif.end()) (*it)->numberOfIf++; } // Delete checks that have numberOfIf >= 2 for (it = checks.begin(); it != checks.end();) { if ((*it)->varId > 0 && (*it)->numberOfIf >= 2) { delete *it; checks.erase(it++); } else { ++it; } } } { tok = check->parse(*tok, checks); if (checks.empty()) return; } if (!tok) break; // return/throw ends all execution paths if (tok->str() == "return" || tok->str() == "throw" || tok->str() == "continue" || tok->str() == "break") { ExecutionPath::bailOut(checks); } } } void checkExecutionPaths(const SymbolDatabase *symbolDatabase, ExecutionPath *c) { for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eFunction || !i->classStart) continue; // Check function std::list checks; checks.push_back(c->copy()); ExecutionPath::checkScope(i->classStart, checks); c->end(checks, i->classEnd); // Cleanup while (!checks.empty()) { delete checks.back(); checks.pop_back(); } } } cppcheck-1.66/lib/executionpath.h000066400000000000000000000102671236713773000170440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef executionpathH #define executionpathH //--------------------------------------------------------------------------- #include #include "config.h" class Token; class Check; class SymbolDatabase; /** * Base class for Execution Paths checking * An execution path is a linear list of statements. There are no "if"/.. to worry about. **/ class CPPCHECKLIB ExecutionPath { private: /** No implementation */ void operator=(const ExecutionPath &); protected: Check * const owner; /** Are two execution paths equal? */ virtual bool is_equal(const ExecutionPath *) const = 0; public: ExecutionPath(Check *c, unsigned int id) : owner(c), numberOfIf(0), varId(id) { } virtual ~ExecutionPath() { } /** Implement this in each derived class. This function must create a copy of the current instance */ virtual ExecutionPath *copy() = 0; /** print checkdata */ void print() const; /** number of if blocks */ unsigned int numberOfIf; const unsigned int varId; /** * bail out all execution paths * @param checks the execution paths to bail out on **/ static void bailOut(std::list &checks) { while (!checks.empty()) { delete checks.back(); checks.pop_back(); } } /** * bail out execution paths with given variable id * @param checks the execution paths to bail out on * @param varid the specific variable id **/ static void bailOutVar(std::list &checks, const unsigned int varid) { if (varid == 0) return; std::list::iterator it = checks.begin(); while (it != checks.end()) { if ((*it)->varId == varid) { delete *it; checks.erase(it++); } else { ++it; } } } /** * Parse tokens at given location * @param tok token to parse * @param checks The execution paths. All execution paths in the list are executed in the current scope. * @return the token before the "next" token. **/ virtual const Token *parse(const Token &tok, std::list &checks) const = 0; /** * Parse condition * @param tok first token in condition. * @param checks The execution paths. All execution paths in the list are executed in the current scope * @return true => bail out all checking **/ virtual bool parseCondition(const Token &tok, std::list &checks); /** * Parse loop body * @param tok the first token in the loop body (the token after the {) * @param checks The execution paths */ virtual void parseLoopBody(const Token *tok, std::list &checks) const { (void)tok; (void)checks; } /** going out of scope - all execution paths end */ virtual void end(const std::list & /*checks*/, const Token * /*tok*/) const { } bool operator==(const ExecutionPath &e) const { return bool(varId == e.varId && is_equal(&e)); } static void checkScope(const Token *tok, std::list &checks); }; void checkExecutionPaths(const SymbolDatabase *symbolDatabase, ExecutionPath *c); //--------------------------------------------------------------------------- #endif // executionpathH cppcheck-1.66/lib/lib.pri000066400000000000000000000067331236713773000153000ustar00rootroot00000000000000# no manual edits - this file is autogenerated by dmake include($$PWD/pcrerules.pri) BASEPATH = ../externals/tinyxml/ include($$PWD/../externals/tinyxml/tinyxml.pri) BASEPATH = ../lib/ INCLUDEPATH += ../externals/tinyxml HEADERS += $${BASEPATH}check.h \ $${BASEPATH}check.h \ $${BASEPATH}check64bit.h \ $${BASEPATH}checkassert.h \ $${BASEPATH}checkassignif.h \ $${BASEPATH}checkautovariables.h \ $${BASEPATH}checkbool.h \ $${BASEPATH}checkboost.h \ $${BASEPATH}checkbufferoverrun.h \ $${BASEPATH}checkclass.h \ $${BASEPATH}checkexceptionsafety.h \ $${BASEPATH}checkinternal.h \ $${BASEPATH}checkio.h \ $${BASEPATH}checkleakautovar.h \ $${BASEPATH}checkmemoryleak.h \ $${BASEPATH}checknonreentrantfunctions.h \ $${BASEPATH}checknullpointer.h \ $${BASEPATH}checkobsoletefunctions.h \ $${BASEPATH}checkother.h \ $${BASEPATH}checkpostfixoperator.h \ $${BASEPATH}checksizeof.h \ $${BASEPATH}checkstl.h \ $${BASEPATH}checkuninitvar.h \ $${BASEPATH}checkunusedfunctions.h \ $${BASEPATH}checkunusedvar.h \ $${BASEPATH}cppcheck.h \ $${BASEPATH}errorlogger.h \ $${BASEPATH}executionpath.h \ $${BASEPATH}library.h \ $${BASEPATH}mathlib.h \ $${BASEPATH}path.h \ $${BASEPATH}preprocessor.h \ $${BASEPATH}settings.h \ $${BASEPATH}suppressions.h \ $${BASEPATH}symboldatabase.h \ $${BASEPATH}templatesimplifier.h \ $${BASEPATH}timer.h \ $${BASEPATH}token.h \ $${BASEPATH}tokenize.h \ $${BASEPATH}tokenlist.h \ $${BASEPATH}valueflow.h \ SOURCES += $${BASEPATH}check.cpp \ $${BASEPATH}check64bit.cpp \ $${BASEPATH}checkassert.cpp \ $${BASEPATH}checkassignif.cpp \ $${BASEPATH}checkautovariables.cpp \ $${BASEPATH}checkbool.cpp \ $${BASEPATH}checkboost.cpp \ $${BASEPATH}checkbufferoverrun.cpp \ $${BASEPATH}checkclass.cpp \ $${BASEPATH}checkexceptionsafety.cpp \ $${BASEPATH}checkinternal.cpp \ $${BASEPATH}checkio.cpp \ $${BASEPATH}checkleakautovar.cpp \ $${BASEPATH}checkmemoryleak.cpp \ $${BASEPATH}checknonreentrantfunctions.cpp \ $${BASEPATH}checknullpointer.cpp \ $${BASEPATH}checkobsoletefunctions.cpp \ $${BASEPATH}checkother.cpp \ $${BASEPATH}checkpostfixoperator.cpp \ $${BASEPATH}checksizeof.cpp \ $${BASEPATH}checkstl.cpp \ $${BASEPATH}checkuninitvar.cpp \ $${BASEPATH}checkunusedfunctions.cpp \ $${BASEPATH}checkunusedvar.cpp \ $${BASEPATH}cppcheck.cpp \ $${BASEPATH}errorlogger.cpp \ $${BASEPATH}executionpath.cpp \ $${BASEPATH}library.cpp \ $${BASEPATH}mathlib.cpp \ $${BASEPATH}path.cpp \ $${BASEPATH}preprocessor.cpp \ $${BASEPATH}settings.cpp \ $${BASEPATH}suppressions.cpp \ $${BASEPATH}symboldatabase.cpp \ $${BASEPATH}templatesimplifier.cpp \ $${BASEPATH}timer.cpp \ $${BASEPATH}token.cpp \ $${BASEPATH}tokenize.cpp \ $${BASEPATH}tokenlist.cpp \ $${BASEPATH}valueflow.cpp cppcheck-1.66/lib/library.cpp000066400000000000000000000510311236713773000161550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "path.h" #include "tinyxml2.h" #include "tokenlist.h" #include "mathlib.h" #include "token.h" #include #include Library::Library() : allocid(0) { } Library::Error Library::load(const char exename[], const char path[]) { if (std::strchr(path,',') != nullptr) { std::string p(path); while (p.find(",") != std::string::npos) { const std::string::size_type pos = p.find(","); 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(); } // 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) == "") { fullfilename += ".cfg"; error = doc.LoadFile(fullfilename.c_str()); } if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { // Try to locate the library configuration in the installation folder.. #ifdef CFGDIR const std::string cfgfolder(CFGDIR); #else if (!exename) return Error(FILE_NOT_FOUND); const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(exename)) + "cfg"); #endif const char *sep = (!cfgfolder.empty() && cfgfolder[cfgfolder.size()-1U]=='/' ? "" : "/"); const std::string filename(cfgfolder + sep + fullfilename); error = doc.LoadFile(filename.c_str()); } } return (error == tinyxml2::XML_NO_ERROR) ? load(doc) : 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_NO_ERROR == 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(BAD_ELEMENT, rootnode->Name()); for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (strcmp(node->Name(),"memory")==0 || strcmp(node->Name(),"resource")==0) { // 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; break; } } } if (allocationId == 0) { if (strcmp(node->Name(), "memory")==0) 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()) { if (strcmp(memorynode->Name(),"alloc")==0) { _alloc[memorynode->GetText()] = allocationId; const char *init = memorynode->Attribute("init"); if (init && strcmp(init,"false")==0) { returnuninitdata.insert(memorynode->GetText()); } } else if (strcmp(memorynode->Name(),"dealloc")==0) _dealloc[memorynode->GetText()] = allocationId; else if (strcmp(memorynode->Name(),"use")==0) use.insert(memorynode->GetText()); else return Error(BAD_ELEMENT, memorynode->Name()); } } else if (strcmp(node->Name(),"define")==0) { 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 (strcmp(node->Name(),"function")==0) { const char *name = node->Attribute("name"); if (name == nullptr) return Error(MISSING_ATTRIBUTE, "name"); for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { if (strcmp(functionnode->Name(),"noreturn")==0) _noreturn[name] = (strcmp(functionnode->GetText(), "true") == 0); else if (strcmp(functionnode->Name(), "pure") == 0) functionpure.insert(name); else if (strcmp(functionnode->Name(), "const") == 0) { functionconst.insert(name); functionpure.insert(name); // a constant function is pure } else if (strcmp(functionnode->Name(),"leak-ignore")==0) leakignore.insert(name); else if (strcmp(functionnode->Name(), "arg") == 0 && functionnode->Attribute("nr") != nullptr) { const bool bAnyArg = strcmp(functionnode->Attribute("nr"),"any")==0; const int nr = (bAnyArg) ? -1 : atoi(functionnode->Attribute("nr")); bool notbool = false; bool notnull = false; bool notuninit = false; bool formatstr = false; bool strz = false; std::string valid; std::list minsizes; for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { if (strcmp(argnode->Name(), "not-bool") == 0) notbool = true; else if (strcmp(argnode->Name(), "not-null") == 0) notnull = true; else if (strcmp(argnode->Name(), "not-uninit") == 0) notuninit = true; else if (strcmp(argnode->Name(), "formatstr") == 0) formatstr = true; else if (strcmp(argnode->Name(), "strz") == 0) strz = true; else if (strcmp(argnode->Name(), "valid") == 0) { // 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 valid = argnode->GetText(); } else if (strcmp(argnode->Name(), "minsize") == 0) { 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); 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); minsizes.back().arg2 = arg2attr[0] - '0'; } } else return Error(BAD_ATTRIBUTE, argnode->Name()); } argumentChecks[name][nr].notbool = notbool; argumentChecks[name][nr].notnull = notnull; argumentChecks[name][nr].notuninit = notuninit; argumentChecks[name][nr].formatstr = formatstr; argumentChecks[name][nr].strz = strz; argumentChecks[name][nr].valid.swap(valid); argumentChecks[name][nr].minsizes.swap(minsizes); } else if (strcmp(functionnode->Name(), "ignorefunction") == 0) { _ignorefunction.insert(name); } else if (strcmp(functionnode->Name(), "formatstr") == 0) { const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan"); const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure"); _formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue()); } else return Error(BAD_ELEMENT, functionnode->Name()); } } else if (strcmp(node->Name(), "reflection") == 0) { for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) { if (strcmp(reflectionnode->Name(), "call") != 0) return Error(BAD_ELEMENT, reflectionnode->Name()); const char * const argString = reflectionnode->Attribute("arg"); if (!argString) return Error(MISSING_ATTRIBUTE, "arg"); _reflection[reflectionnode->GetText()] = atoi(argString); } } else if (strcmp(node->Name(), "markup") == 0) { const char * const extension = node->Attribute("ext"); if (!extension) return Error(MISSING_ATTRIBUTE, "ext"); _markupExtensions.insert(extension); const char * const reporterrors = node->Attribute("reporterrors"); _reporterrors[extension] = (reporterrors && strcmp(reporterrors, "true") == 0); const char * const aftercode = node->Attribute("aftercode"); _processAfterCode[extension] = (aftercode && strcmp(aftercode, "true") == 0); for (const tinyxml2::XMLElement *markupnode = node->FirstChildElement(); markupnode; markupnode = markupnode->NextSiblingElement()) { if (strcmp(markupnode->Name(), "keywords") == 0) { for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { if (strcmp(librarynode->Name(), "keyword") == 0) _keywords[extension].insert(librarynode->Attribute("name")); else return Error(BAD_ELEMENT, librarynode->Name()); } } else if (strcmp(markupnode->Name(), "exported") == 0) { for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) { if (strcmp(exporter->Name(), "exporter") != 0) return Error(BAD_ELEMENT, exporter->Name()); 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()) { if (strcmp(e->Name(), "prefix") == 0) _exporters[prefix].addPrefix(e->GetText()); else if (strcmp(e->Name(), "suffix") == 0) _exporters[prefix].addSuffix(e->GetText()); else return Error(BAD_ELEMENT, e->Name()); } } } else if (strcmp(markupnode->Name(), "imported") == 0) { for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { if (strcmp(librarynode->Name(), "importer") == 0) _importers[extension].insert(librarynode->GetText()); else return Error(BAD_ELEMENT, librarynode->Name()); } } else if (strcmp(markupnode->Name(), "codeblocks") == 0) { for (const tinyxml2::XMLElement *blocknode = markupnode->FirstChildElement(); blocknode; blocknode = blocknode->NextSiblingElement()) { if (strcmp(blocknode->Name(), "block") == 0) { const char * name = blocknode->Attribute("name"); if (name) _executableblocks[extension].addBlock(blocknode->Attribute("name")); } else if (strcmp(blocknode->Name(), "structure") == 0) { 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 return Error(BAD_ELEMENT, blocknode->Name()); } } else return Error(BAD_ELEMENT, markupnode->Name()); } } else if (strcmp(node->Name(), "podtype") == 0) { const char * const name = node->Attribute("name"); if (!name) return Error(MISSING_ATTRIBUTE, "name"); PodType podType = {0}; const char * const size = node->Attribute("sizeof"); if (size) podType.size = atoi(size); const char * const sign = node->Attribute("sign"); if (sign) podType.sign = *sign; podtypes[name] = podType; } else return Error(BAD_ELEMENT, node->Name()); } return Error(OK); } bool Library::isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const { const ArgumentChecks *ac = getarg(functionName, argnr); if (!ac || ac->valid.empty()) return true; TokenList tokenList(0); 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; } const Library::ArgumentChecks * Library::getarg(const std::string &functionName, int argnr) const { std::map >::const_iterator it1; it1 = argumentChecks.find(functionName); if (it1 == argumentChecks.end()) return nullptr; const std::map::const_iterator it2 = it1->second.find(argnr); if (it2 != it1->second.end()) return &it2->second; const std::map::const_iterator it3 = it1->second.find(-1); if (it3 != it1->second.end()) 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 (funcname && Token::Match(funcname->tokAt(-3),"( * %var% )")) { funcname = funcname->previous(); start = funcname->tokAt(-3); } else if (funcname->isName()) { while (Token::Match(start, "%var%|.|::")) start = start->previous(); } else { return false; } if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%var% )| (")) { if (funcname->str() == "exit") return true; if (!isnotnoreturn(funcname->str())) { if (unknownFunc && !isnoreturn(funcname->str())) *unknownFunc = funcname->str(); return true; } } return false; } cppcheck-1.66/lib/library.h000066400000000000000000000344021236713773000156250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "path.h" #include "mathlib.h" #include "token.h" #include #include #include #include class TokenList; namespace tinyxml2 { class XMLDocument; } /// @addtogroup Core /// @{ /** * @brief Library definitions handling */ class CPPCHECKLIB Library { public: Library(); enum ErrorCode { OK, FILE_NOT_FOUND, BAD_XML, BAD_ELEMENT, MISSING_ATTRIBUTE, BAD_ATTRIBUTE, BAD_ATTRIBUTE_VALUE }; class Error { public: Error() : errorcode(OK) , reason("") {} explicit Error(ErrorCode e) : errorcode(e) , reason("") {} Error(ErrorCode e, const std::string &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); /** get allocation id for function by name */ int alloc(const char name[]) const { return getid(_alloc, name); } /** get allocation id for function */ int alloc(const Token *tok) const { return tok->function() ? 0 : getid(_alloc, tok->str()); } /** get deallocation id for function */ int dealloc(const Token *tok) const { return tok->function() ? 0 : getid(_dealloc, tok->str()); } /** get deallocation id for function by name */ int dealloc(const char name[]) const { return getid(_dealloc, name); } /** set allocation id for function */ void setalloc(const std::string &functionname, int id) { _alloc[functionname] = id; } void setdealloc(const std::string &functionname, int id) { _dealloc[functionname] = id; } /** 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)); } /** is allocation type resource? */ static bool isresource(int id) { return ((id > 0) && ((id & 1) == 1)); } bool formatstr_function(const std::string& funcname) const { return _formatstr.find(funcname) != _formatstr.end(); } bool formatstr_scan(const std::string& funcname) const { return _formatstr.at(funcname).first; } bool formatstr_secure(const std::string& funcname) const { return _formatstr.at(funcname).second; } std::set use; std::set leakignore; std::set functionconst; std::set functionpure; bool isnoreturn(const std::string &name) const { std::map::const_iterator it = _noreturn.find(name); return (it != _noreturn.end() && it->second); } bool isnotnoreturn(const std::string &name) const { std::map::const_iterator it = _noreturn.find(name); return (it != _noreturn.end() && !it->second); } bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const; class ArgumentChecks { public: ArgumentChecks() : notbool(false), notnull(false), notuninit(false), formatstr(false), strz(false) { } bool notbool; bool notnull; bool notuninit; bool formatstr; bool strz; std::string valid; 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::list minsizes; }; // function name, argument nr => argument data std::map > argumentChecks; bool isboolargbad(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg && arg->notbool; } bool isnullargbad(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg && arg->notnull; } bool isuninitargbad(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg && arg->notuninit; } bool isargformatstr(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg && arg->formatstr; } bool isargstrz(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg && arg->strz; } bool isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const; const std::string& validarg(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg ? arg->valid : emptyString; } bool hasminsize(const std::string &functionName) const { std::map >::const_iterator it1; it1 = argumentChecks.find(functionName); if (it1 == argumentChecks.end()) return false; std::map::const_iterator it2; for (it2 = it1->second.begin(); it2 != it1->second.end(); ++it2) { if (!it2->second.minsizes.empty()) return true; } return false; } const std::list *argminsizes(const std::string &functionName, int argnr) const { const ArgumentChecks *arg = getarg(functionName, argnr); return arg ? &arg->minsizes : nullptr; } bool markupFile(const std::string &path) const { return _markupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != _markupExtensions.end(); } bool processMarkupAfterCode(const std::string &path) const { const std::map::const_iterator it = _processAfterCode.find(Path::getFilenameExtensionInLowerCase(path)); return (it == _processAfterCode.end() || it->second); } const std::set &markupExtensions() const { return _markupExtensions; } bool reportErrors(const std::string &path) const { const std::map::const_iterator it = _reporterrors.find(Path::getFilenameExtensionInLowerCase(path)); return (it == _reporterrors.end() || it->second); } bool ignorefunction(const std::string &function) const { return (_ignorefunction.find(function) != _ignorefunction.end()); } bool 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 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& 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& 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 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 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 { const std::map >::const_iterator it = _importers.find(Path::getFilenameExtensionInLowerCase(file)); return (it != _importers.end() && it->second.count(importer) > 0); } bool isreflection(const std::string &token) const { const std::map::const_iterator it = _reflection.find(token); return it != _reflection.end(); } int reflectionArgument(const std::string &token) const { int argIndex = -1; const std::map::const_iterator it = _reflection.find(token); if (it != _reflection.end()) { argIndex = it->second; } return argIndex; } 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; } private: 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 std::string& s) { _start = s; } void setEnd(const std::string& e) { _end = e; } void setOffset(const int o) { _offset = o; } void addBlock(const std::string& 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::map _alloc; // allocation functions std::map _dealloc; // deallocation functions std::map _noreturn; // is function noreturn? std::set _ignorefunction; // ignore functions/macros from a library (gtk, qt etc) 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 > _formatstr; // Parameters for format string checking std::map podtypes; // pod types const ArgumentChecks * getarg(const std::string &functionName, int argnr) const; static int getid(const std::map &data, const std::string &name) { const std::map::const_iterator it = data.find(name); return (it == data.end()) ? 0 : it->second; } }; /// @} //--------------------------------------------------------------------------- #endif // libraryH cppcheck-1.66/lib/mathlib.cpp000066400000000000000000000505451236713773000161420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include MathLib::biguint MathLib::toULongNumber(const std::string & str) { // hexadecimal numbers: if (isHex(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; } MathLib::bigint MathLib::toLongNumber(const std::string & str) { // hexadecimal numbers: if (isHex(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 = std::atof(str.c_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); } bigint ret = 0; std::istringstream istr(str); istr >> ret; return ret; } double MathLib::toDoubleNumber(const std::string &str) { if (isHex(str)) return static_cast(toLongNumber(str)); // nullcheck else if (isNullValue(str)) return 0.0; // otherwise, convert to double std::istringstream istr(str); 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 &s) { if (s.empty()) return false; enum State { START, BASE_PLUSMINUS, BASE_DIGITS1, LEADING_DECIMAL, TRAILING_DECIMAL, BASE_DIGITS2, E, MANTISSA_PLUSMINUS, MANTISSA_DIGITS, F } state = START; for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) { switch (state) { case START: if (*it=='+' || *it=='-') state=BASE_PLUSMINUS; else if (*it=='.') state=LEADING_DECIMAL; else if (std::isdigit(*it)) state=BASE_DIGITS1; else return false; break; case BASE_PLUSMINUS: if (*it=='.') state=LEADING_DECIMAL; else if (std::isdigit(*it)) state=BASE_DIGITS1; else if (*it=='e' || *it=='E') state=E; else return false; break; case LEADING_DECIMAL: if (std::isdigit(*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(*it)) return false; break; case TRAILING_DECIMAL: if (*it=='e' || *it=='E') state=E; else if (std::isdigit(*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=F; else if (!std::isdigit(*it)) return false; break; case E: if (*it=='+' || *it=='-') state=MANTISSA_PLUSMINUS; else if (std::isdigit(*it)) state=MANTISSA_DIGITS; else return false; break; case MANTISSA_PLUSMINUS: if (!std::isdigit(*it)) return false; else state=MANTISSA_DIGITS; break; case MANTISSA_DIGITS: if (*it=='f' || *it=='F') state=F; else if (!std::isdigit(*it)) return false; break; case F: return false; } } return (state==BASE_DIGITS2 || state==MANTISSA_DIGITS || state==TRAILING_DECIMAL || state==F); } bool MathLib::isNegative(const std::string &s) { // remember position std::string::size_type n = 0; // eat up whitespace while (std::isspace(s[n])) ++n; // every negative number has a negative sign return (s[n] == '-'); } bool MathLib::isPositive(const std::string &s) { return !MathLib::isNegative(s); } /*! \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[in] s 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& s) { enum Status { START, PLUSMINUS, OCTAL_PREFIX, DIGITS } state = START; for (std::string::const_iterator it = s.begin(); it != s.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(*it)) state = DIGITS; else return false; break; case DIGITS: if (isOctalDigit(*it)) state = DIGITS; else return isValidSuffix(it,s.end()); break; } } return state == DIGITS; } bool MathLib::isHex(const std::string& s) { enum Status { START, PLUSMINUS, HEX_PREFIX, DIGIT, DIGITS } state = START; for (std::string::const_iterator it = s.begin(); it != s.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(*it)) state = DIGITS; else return false; break; case DIGITS: if (isxdigit(*it)) state = DIGITS; else return isValidSuffix(it,s.end()); break; } } return state == DIGITS; } bool MathLib::isValidSuffix(std::string::const_iterator it, std::string::const_iterator end) { enum Status { START, SUFFIX_U, SUFFIX_UL, SUFFIX_ULL, SUFFIX_L, SUFFIX_LU, SUFFIX_LL, SUFFIX_LLU } 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 return false; break; case SUFFIX_U: if (*it == 'l' || *it == 'L') state = SUFFIX_UL; // UL 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; 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); } /*! \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[in] s 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& s) { enum Status { START, PLUSMINUS, GNU_BIN_PREFIX, DIGIT, DIGITS } state = START; for (std::string::const_iterator it = s.begin(); it != s.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 isValidSuffix(it,s.end()); break; } } return state == DIGITS; } bool MathLib::isDec(const std::string & s) { enum Status { START, PLUSMINUS, DIGIT, SUFFIX } state = START; for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) { switch (state) { case START: if (*it == '+' || *it == '-') state = PLUSMINUS; else if (isdigit(*it)) state = DIGIT; else return false; break; case PLUSMINUS: if (isdigit(*it)) state = DIGIT; else return false; break; case DIGIT: if (isdigit(*it)) state = DIGIT; else return isValidSuffix(it,s.end()); break; case SUFFIX: break; } } return state == DIGIT; } bool MathLib::isInt(const std::string & s) { return isDec(s) || isHex(s) || isOct(s); } std::string MathLib::add(const std::string & first, const std::string & second) { if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) + toLongNumber(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); } std::string MathLib::subtract(const std::string &first, const std::string &second) { if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) - toLongNumber(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); } std::string MathLib::incdec(const std::string & var, const std::string & op) { if (op == "++") return MathLib::add(var, "1"); else if (op == "--") return MathLib::subtract(var, "1"); throw InternalError(0, 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) { if (MathLib::isInt(first) && MathLib::isInt(second)) { const bigint a = toLongNumber(first); const bigint b = toLongNumber(second); if (a == std::numeric_limits::min()) throw InternalError(0, "Internal Error: Division overflow"); if (b == 0) throw InternalError(0, "Internal Error: Division by zero"); return toString(toLongNumber(first) / b); } else if (isNullValue(second)) { if (isNullValue(first)) return "nan.0"; const int sign_first = (isPositive(first)) ? 1 : -1; const int sign_second = (isPositive(second)) ? 1 : -1; return (sign_first*sign_second == 1) ? "inf.0" : "-inf.0"; } return toString(toDoubleNumber(first) / toDoubleNumber(second)); } std::string MathLib::multiply(const std::string &first, const std::string &second) { if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) * toLongNumber(second)); } return toString(toDoubleNumber(first) * toDoubleNumber(second)); } std::string MathLib::mod(const std::string &first, const std::string &second) { if (MathLib::isInt(first) && MathLib::isInt(second)) { bigint b = toLongNumber(second); if (b == 0) throw InternalError(0, "Internal Error: Division by zero"); return toString(toLongNumber(first) % b); } return toString(std::fmod(toDoubleNumber(first),toDoubleNumber(second))); } 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)); case '|': return MathLib::toString(MathLib::toLongNumber(first) | MathLib::toLongNumber(second)); case '^': return MathLib::toString(MathLib::toLongNumber(first) ^ MathLib::toLongNumber(second)); default: throw InternalError(0, 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'); } cppcheck-1.66/lib/mathlib.h000066400000000000000000000075241236713773000156060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include "config.h" /// @addtogroup Core /// @{ /** @brief simple math functions that uses operands stored in std::string. useful when performing math on tokens. */ class CPPCHECKLIB MathLib { public: typedef long long bigint; typedef unsigned long long biguint; 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 isNegative(const std::string &str); static bool isPositive(const std::string &str); static bool isDec(const std::string & str); static bool isHex(const std::string& str); static bool isOct(const std::string& str); static bool isBin(const std::string& str); static bool isValidSuffix(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 &tok); /** * Return true if given character is 0,1,2,3,4,5,6 or 7. * @param c The character to check * @return true if given character is octal digit. */ static bool isOctalDigit(char c); }; template<> CPPCHECKLIB std::string MathLib::toString(double value); // Declare specialization to avoid linker problems /// @} //--------------------------------------------------------------------------- #endif // mathlibH cppcheck-1.66/lib/path.cpp000066400000000000000000000155241236713773000154540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 #include #include #include #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) char separ = '/'; char native = '\\'; #else char separ = '\\'; char native = '/'; #endif std::replace(path.begin(), path.end(), separ, native); return path; } std::string Path::fromNativeSeparators(std::string path) { char nonnative = '\\'; char newsepar = '/'; std::replace(path.begin(), path.end(), nonnative, newsepar); return path; } std::string Path::simplifyPath(std::string originalPath) { // Skip ./ at the beginning if (originalPath.size() > 2 && originalPath[0] == '.' && originalPath[1] == '/') { originalPath = originalPath.erase(0, 2); } std::string subPath = ""; std::vector pathParts; for (std::size_t i = 0; i < originalPath.size(); ++i) { if (originalPath[i] == '/' || originalPath[i] == '\\') { if (subPath.length() > 0) { pathParts.push_back(subPath); subPath = ""; } pathParts.push_back(std::string(1 , originalPath[i])); } else subPath.append(1, originalPath[i]); } if (subPath.length() > 0) pathParts.push_back(subPath); for (unsigned int i = 1; i < pathParts.size(); ++i) { if (i > 1 && pathParts[i-2] != ".." && pathParts[i] == ".." && pathParts.size() > i + 1) { pathParts.erase(pathParts.begin() + static_cast(i) + 1); pathParts.erase(pathParts.begin() + static_cast(i)); pathParts.erase(pathParts.begin() + static_cast(i) - 1); pathParts.erase(pathParts.begin() + static_cast(i) - 2); i = 0; } else if (i > 0 && pathParts[i] == ".") { pathParts.erase(pathParts.begin() + static_cast(i)); i = 0; } else if (i > 0 && pathParts[i] == "/" && pathParts[i-1] == "/") { pathParts.erase(pathParts.begin() + static_cast(i) - 1); i = 0; } } std::ostringstream oss; for (std::vector::size_type i = 0; i < pathParts.size(); ++i) { oss << pathParts[i]; } return oss.str(); } std::string Path::getPathFromFilename(const std::string &filename) { std::string path = ""; std::size_t pos = filename.find_last_of("\\/"); if (pos != std::string::npos) path = filename.substr(0, 1 + pos); return path; } bool Path::sameFileName(const std::string &fname1, const std::string &fname2) { #if defined(__linux__) || defined(__sun) || defined(__hpux) return bool(fname1 == fname2); #elif defined(_MSC_VER) || (defined(__GNUC__) && defined(_WIN32)) return bool(_stricmp(fname1.c_str(), fname2.c_str()) == 0); #elif defined(__GNUC__) return bool(strcasecmp(fname1.c_str(), fname2.c_str()) == 0); #elif defined(__BORLANDC__) return bool(stricmp(fname1.c_str(), fname2.c_str()) == 0); #else #error Platform filename compare function needed #endif } // 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::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 = (*i)[i->length()-1] == '/'; 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"); } bool Path::isCPP(const std::string &path) { const std::string extension = getFilenameExtensionInLowerCase(path); if (extension == ".cpp" || extension == ".cxx" || extension == ".cc" || extension == ".c++" || extension == ".hpp" || extension == ".tpp" || extension == ".txx") { return true; } // In unix, ".C" is considered C++ file return (getFilenameExtension(path) == ".C"); } 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); } cppcheck-1.66/lib/path.h000066400000000000000000000130661236713773000151200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 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 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 filename 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 &filename, 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 extensionInLowerCase filename to check. path info is optional * @return true if extension is meant for C++ files */ static bool isCPP(const std::string &extensionInLowerCase); private: /** * @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); }; /// @} //--------------------------------------------------------------------------- #endif // pathH cppcheck-1.66/lib/pcrerules.pri000066400000000000000000000007241236713773000165300ustar00rootroot00000000000000# 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.66/lib/preprocessor.cpp000066400000000000000000003511021236713773000172410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "token.h" #include "path.h" #include "errorlogger.h" #include "settings.h" #include #include #include #include #include #include #include #include bool Preprocessor::missingIncludeFlag; bool Preprocessor::missingSystemIncludeFlag; char Preprocessor::macroChar = char(1); Preprocessor::Preprocessor(Settings *settings, ErrorLogger *errorLogger) : _settings(settings), _errorLogger(errorLogger), _foundUnhandledChars(false) { } void Preprocessor::writeError(const std::string &fileName, const unsigned int linenr, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText) { if (!errorLogger) return; std::list locationList; ErrorLogger::ErrorMessage::FileLocation loc; loc.line = linenr; loc.setfile(fileName); locationList.push_back(loc); errorLogger->reportErr(ErrorLogger::ErrorMessage(locationList, Severity::error, errorText, errorType, false)); } 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) { unsigned char ch2 = (unsigned char)istr.get(); 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; } // Concatenates a list of strings, inserting a separator between parts static std::string join(const std::set& list, char separator) { std::string s; for (std::set::const_iterator it = list.begin(); it != list.end(); ++it) { if (!s.empty()) s += separator; s += *it; } return s; } // Removes duplicate string portions separated by the specified separator static std::string unify(const std::string &s, char separator) { std::set parts; std::string::size_type prevPos = 0; for (std::string::size_type pos = 0; pos < s.length(); ++pos) { if (s[pos] == separator) { if (pos > prevPos) parts.insert(s.substr(prevPos, pos - prevPos)); prevPos = pos + 1; } } if (prevPos < s.length()) parts.insert(s.substr(prevPos)); return join(parts, separator); } bool Preprocessor::cplusplus(const Settings *settings, const std::string &filename) { const bool undef = settings && settings->userUndefs.find("__cplusplus") != settings->userUndefs.end(); const bool cpplang = settings && settings->enforcedLang == Settings::CPP; const bool cppfile = (!settings || settings->enforcedLang == Settings::None) && Path::isCPP(filename); return (!undef && (cpplang || cppfile)); } /** * Get cfgmap - a map of macro names and values */ static std::map getcfgmap(const std::string &cfg, const Settings *settings, const std::string &filename) { std::map cfgmap; if (!cfg.empty()) { std::string::size_type pos = 0; for (;;) { std::string::size_type pos2 = cfg.find_first_of(";=", pos); if (pos2 == std::string::npos) { cfgmap[cfg.substr(pos)] = ""; break; } if (cfg[pos2] == ';') { cfgmap[cfg.substr(pos, pos2-pos)] = ""; } else { std::string::size_type pos3 = pos2; pos2 = cfg.find(";", pos2); if (pos2 == std::string::npos) { cfgmap[cfg.substr(pos, pos3-pos)] = cfg.substr(pos3 + 1); break; } else { cfgmap[cfg.substr(pos, pos3-pos)] = cfg.substr(pos3 + 1, pos2 - pos3 - 1); } } pos = pos2 + 1; } } if (cfgmap.find("__cplusplus") == cfgmap.end() && Preprocessor::cplusplus(settings,filename)) cfgmap["__cplusplus"] = "1"; return cfgmap; } /** Just read the code into a string. Perform simple cleanup of the code */ std::string Preprocessor::read(std::istream &istr, const std::string &filename) { // The UTF-16 BOM is 0xfffe or 0xfeff. unsigned int bom = 0; if (istr.peek() >= 0xfe) { bom = ((unsigned int)istr.get() << 8); if (istr.peek() >= 0xfe) bom |= (unsigned int)istr.get(); else bom = 0; // allowed boms are 0/0xfffe/0xfeff } if (_settings && _settings->terminated()) return ""; if (_settings && _settings->checkConfiguration) return readpreprocessor(istr,bom); // ------------------------------------------------------------------------------------------ // // handling // when this is encountered the will be "skipped". // on the next , extra newlines will be added std::ostringstream code; unsigned int newlines = 0; for (unsigned char ch = readChar(istr,bom); istr.good(); ch = readChar(istr,bom)) { // Replace assorted special chars with spaces.. if (((ch & 0x80) == 0) && (ch != '\n') && (std::isspace(ch) || std::iscntrl(ch))) ch = ' '; // .. // for gcc-compatibility the trailing spaces should be ignored // for vs-compatibility the trailing spaces should be kept // See tickets #640 and #1869 // The solution for now is to have a compiler-dependent behaviour. if (ch == '\\') { unsigned char chNext; std::string spaces; #ifdef __GNUC__ // gcc-compatibility: ignore spaces for (;; spaces += ' ') { chNext = (unsigned char)istr.peek(); if (chNext != '\n' && chNext != '\r' && (std::isspace(chNext) || std::iscntrl(chNext))) { // Skip whitespace between and (void)readChar(istr,bom); continue; } break; } #else // keep spaces chNext = (unsigned char)istr.peek(); #endif if (chNext == '\n' || chNext == '\r') { ++newlines; (void)readChar(istr,bom); // Skip the "" } else { code << "\\" << spaces; } } else { code << char(ch); // if there has been sequences, add extra newlines.. if (ch == '\n' && newlines > 0) { code << std::string(newlines, '\n'); newlines = 0; } } } std::string result = code.str(); code.str(""); // ------------------------------------------------------------------------------------------ // // Remove all comments.. result = removeComments(result, filename); if (_settings && _settings->terminated()) return ""; // ------------------------------------------------------------------------------------------ // // Clean up all preprocessor statements result = preprocessCleanupDirectives(result); if (_settings && _settings->terminated()) return ""; // ------------------------------------------------------------------------------------------ // // Clean up preprocessor #if statements with Parentheses result = removeParentheses(result); if (_settings && _settings->terminated()) return ""; // Remove '#if 0' blocks if (result.find("#if 0\n") != std::string::npos) result = removeIf0(result); if (_settings && _settings->terminated()) return ""; return result; } /** read preprocessor statements */ std::string Preprocessor::readpreprocessor(std::istream &istr, const unsigned int bom) { enum { NEWLINE, SPACE, PREPROCESSOR, BACKSLASH, OTHER } state = NEWLINE; std::ostringstream code; unsigned int newlines = 1; unsigned char chPrev = ' '; for (unsigned char ch = readChar(istr,bom); istr.good(); ch = readChar(istr,bom)) { // Replace assorted special chars with spaces.. if (((ch & 0x80) == 0) && (ch != '\n') && (std::isspace(ch) || std::iscntrl(ch))) ch = ' '; if (ch == ' ' && chPrev == ' ') continue; if (state == PREPROCESSOR && chPrev == '/' && (ch == '/' || ch == '*')) state = OTHER; chPrev = ch; if (ch == '\n') { if (state != BACKSLASH) { state = NEWLINE; code << std::string(newlines, '\n'); newlines = 1; } else { ++newlines; state = PREPROCESSOR; } continue; } switch (state) { case NEWLINE: if (ch==' ') state = SPACE; else if (ch == '#') { state = PREPROCESSOR; code << ch; } else state = OTHER; break; case SPACE: if (ch == '#') { state = PREPROCESSOR; code << ch; } else if (ch != ' ') state = OTHER; break; case PREPROCESSOR: code << ch; if (ch == '\\') state = BACKSLASH; break; case BACKSLASH: code << ch; if (ch != ' ') state = PREPROCESSOR; break; case OTHER: break; }; } std::string result = preprocessCleanupDirectives(code.str()); result = removeParentheses(result); return removeIf0(result); } std::string Preprocessor::preprocessCleanupDirectives(const std::string &processedFile) { std::ostringstream code; std::istringstream sstr(processedFile); std::string line; while (std::getline(sstr, line)) { // Trim lines.. if (!line.empty() && line[0] == ' ') line.erase(0, line.find_first_not_of(" ")); if (!line.empty() && line[line.size()-1] == ' ') line.erase(line.find_last_not_of(" ") + 1); // Preprocessor if (!line.empty() && line[0] == '#') { enum { ESC_NONE, ESC_SINGLE, ESC_DOUBLE } escapeStatus = ESC_NONE; char prev = ' '; // hack to make it skip spaces between # and the directive code << "#"; std::string::const_iterator i = line.begin(); ++i; // need space.. #if( => #if ( bool needSpace = true; while (i != line.end()) { // disable esc-mode if (escapeStatus != ESC_NONE) { if (prev != '\\' && escapeStatus == ESC_SINGLE && *i == '\'') { escapeStatus = ESC_NONE; } if (prev != '\\' && escapeStatus == ESC_DOUBLE && *i == '"') { escapeStatus = ESC_NONE; } } else { // enable esc-mode if (escapeStatus == ESC_NONE && *i == '"') escapeStatus = ESC_DOUBLE; if (escapeStatus == ESC_NONE && *i == '\'') escapeStatus = ESC_SINGLE; } // skip double whitespace between arguments if (escapeStatus == ESC_NONE && prev == ' ' && *i == ' ') { ++i; continue; } // Convert #if( to "#if (" if (escapeStatus == ESC_NONE) { if (needSpace) { if (*i == '(' || *i == '!') code << " "; else if (!std::isalpha((unsigned char)*i)) needSpace = false; } if (*i == '#') needSpace = true; } code << *i; if (escapeStatus != ESC_NONE && prev == '\\' && *i == '\\') { prev = ' '; } else { prev = *i; } ++i; } if (escapeStatus != ESC_NONE) { // unmatched quotes.. compiler should probably complain about this.. } } else { // Do not mess with regular code.. code << line; } code << (sstr.eof()?"":"\n"); } return code.str(); } static bool hasbom(const std::string &str) { return bool(str.size() >= 3 && static_cast(str[0]) == 0xef && static_cast(str[1]) == 0xbb && static_cast(str[2]) == 0xbf); } // 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); } static bool isFallThroughComment(std::string comment) { // convert comment to lower case without whitespace for (std::string::iterator i = comment.begin(); i != comment.end();) { if (std::isspace(static_cast(*i))) i = comment.erase(i); else ++i; } std::transform(comment.begin(), comment.end(), comment.begin(), tolowerWrapper); return comment.find("fallthr") != std::string::npos || comment.find("fallsthr") != std::string::npos || comment.find("fall-thr") != std::string::npos || comment.find("dropthr") != std::string::npos || comment.find("passthr") != std::string::npos || comment.find("nobreak") != std::string::npos || comment == "fall"; } std::string Preprocessor::removeComments(const std::string &str, const std::string &filename) { // For the error report unsigned int lineno = 1; // handling // when this is encountered the will be "skipped". // on the next , extra newlines will be added unsigned int newlines = 0; std::ostringstream code; unsigned char previous = 0; bool inPreprocessorLine = false; std::vector suppressionIDs; bool fallThroughComment = false; for (std::string::size_type i = hasbom(str) ? 3U : 0U; i < str.length(); ++i) { unsigned char ch = static_cast(str[i]); if (ch & 0x80) { std::ostringstream errmsg; errmsg << "The code contains characters that are unhandled. " << "Neither unicode nor extended ASCII are supported. " << "(line=" << lineno << ", character code=" << std::hex << (int(ch) & 0xff) << ")"; writeError(filename, lineno, _errorLogger, "syntaxError", errmsg.str()); _foundUnhandledChars = true; } if (_settings && _settings->terminated()) return ""; if (str.compare(i, 7, "#error ") == 0 || str.compare(i, 9, "#warning ") == 0) { if (str.compare(i, 6, "#error") == 0) code << "#error"; i = str.find("\n", i); if (i == std::string::npos) break; --i; continue; } // First skip over any whitespace that may be present if (std::isspace(ch)) { if (ch == ' ' && previous == ' ') { // Skip double white space } else { code << char(ch); previous = ch; } // if there has been sequences, add extra newlines.. if (ch == '\n') { if (previous != '\\') inPreprocessorLine = false; ++lineno; if (newlines > 0) { code << std::string(newlines, '\n'); newlines = 0; previous = '\n'; } } continue; } // Remove comments.. if (str.compare(i, 2, "//") == 0) { std::size_t commentStart = i + 2; i = str.find('\n', i); if (i == std::string::npos) break; std::string comment(str, commentStart, i - commentStart); if (_settings && _settings->_inlineSuppressions) { std::istringstream iss(comment); std::string word; iss >> word; if (word == "cppcheck-suppress") { iss >> word; if (iss) suppressionIDs.push_back(word); } } if (isFallThroughComment(comment)) { fallThroughComment = true; } code << "\n"; previous = '\n'; ++lineno; } else if (str.compare(i, 2, "/*") == 0) { std::size_t commentStart = i + 2; unsigned char chPrev = 0; ++i; while (i < str.length() && (chPrev != '*' || ch != '/')) { chPrev = ch; ++i; ch = static_cast(str[i]); if (ch == '\n') { ++newlines; ++lineno; } } std::string comment(str, commentStart, i - commentStart - 1); if (isFallThroughComment(comment)) { fallThroughComment = true; } if (_settings && _settings->_inlineSuppressions) { std::istringstream iss(comment); std::string word; iss >> word; if (word == "cppcheck-suppress") { iss >> word; if (iss) suppressionIDs.push_back(word); } } } else if ((i == 0 || std::isspace((unsigned char)str[i-1])) && str.compare(i, 5, "__asm") == 0) { while (i < str.size() && (std::isalpha((unsigned char)str[i]) || str[i] == '_')) code << str[i++]; while (i < str.size() && std::isspace((unsigned char)str[i])) code << str[i++]; if (str[i] == '{') { // Ticket 4873: Extract comments from the __asm / __asm__'s content std::string asmBody; while (i < str.size() && str[i] != '}') { if (str[i] == ';') { std::string::size_type backslashN = str.find("\n", i); if (backslashN != std::string::npos) // Ticket #4922: Don't go in infinite loop or crash if there is no '\n' i = backslashN; } asmBody += str[i++]; } code << removeComments(asmBody, filename); code << '}'; } else --i; } else if (ch == '#' && previous == '\n') { code << ch; previous = ch; inPreprocessorLine = true; // Add any pending inline suppressions that have accumulated. if (!suppressionIDs.empty()) { if (_settings != nullptr) { // Add the suppressions. for (std::size_t j = 0; j < suppressionIDs.size(); ++j) { const std::string errmsg(_settings->nomsg.addSuppression(suppressionIDs[j], filename, lineno)); if (!errmsg.empty()) { writeError(filename, lineno, _errorLogger, "cppcheckError", errmsg); } } } suppressionIDs.clear(); } } else { if (!inPreprocessorLine) { // Not whitespace, not a comment, and not preprocessor. // Must be code here! // First check for a "fall through" comment match, but only // add a suppression if the next token is 'case' or 'default' if (_settings && _settings->isEnabled("style") && _settings->experimental && fallThroughComment) { std::string::size_type j = str.find_first_not_of("abcdefghijklmnopqrstuvwxyz", i); std::string tok = str.substr(i, j - i); if (tok == "case" || tok == "default") suppressionIDs.push_back("switchCaseFallThrough"); fallThroughComment = false; } // Add any pending inline suppressions that have accumulated. if (!suppressionIDs.empty()) { if (_settings != nullptr) { // Relative filename std::string relativeFilename(filename); 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::size_t j = 0; j < suppressionIDs.size(); ++j) { const std::string errmsg(_settings->nomsg.addSuppression(suppressionIDs[j], relativeFilename, lineno)); if (!errmsg.empty()) { writeError(filename, lineno, _errorLogger, "cppcheckError", errmsg); } } } suppressionIDs.clear(); } } // String or char constants.. if (ch == '\"' || ch == '\'') { code << char(ch); char chNext; do { ++i; chNext = str[i]; if (chNext == '\\') { ++i; char chSeq = str[i]; if (chSeq == '\n') ++newlines; else { code << chNext; code << chSeq; previous = static_cast(chSeq); } } else { code << chNext; previous = static_cast(chNext); } } while (i < str.length() && chNext != ch && chNext != '\n'); } // Rawstring.. else if (str.compare(i,2,"R\"")==0) { std::string delim; for (std::string::size_type i2 = i+2; i2 < str.length(); ++i2) { if (i2 > 16 || std::isspace(str[i2]) || std::iscntrl(str[i2]) || str[i2] == ')' || str[i2] == '\\') { delim = " "; break; } else if (str[i2] == '(') break; delim += str[i2]; } const std::string::size_type endpos = str.find(")" + delim + "\"", i); if (delim != " " && endpos != std::string::npos) { unsigned int rawstringnewlines = 0; code << '\"'; for (std::string::size_type p = i + 3 + delim.size(); p < endpos; ++p) { if (str[p] == '\n') { rawstringnewlines++; code << "\\n"; } else if (std::iscntrl((unsigned char)str[p]) || std::isspace((unsigned char)str[p])) { code << " "; } else if (str[p] == '\"' || str[p] == '\'') { code << "\\" << (char)str[p]; } else { code << (char)str[p]; } } code << "\""; if (rawstringnewlines > 0) code << std::string(rawstringnewlines, '\n'); i = endpos + delim.size() + 1; } else { code << "R"; previous = 'R'; } } else { code << char(ch); previous = ch; } } } return code.str(); } std::string Preprocessor::removeIf0(const std::string &code) { std::ostringstream ret; std::istringstream istr(code); std::string line; while (std::getline(istr,line)) { ret << line << "\n"; if (line == "#if 0") { // goto the end of the '#if 0' block unsigned int level = 1; bool in = false; while (level > 0 && std::getline(istr,line)) { if (line.compare(0,3,"#if") == 0) ++level; else if (line == "#endif") --level; else if ((line == "#else") || (line.compare(0, 5, "#elif") == 0)) { if (level == 1) in = true; } else { if (in) ret << line << "\n"; else // replace code within '#if 0' block with empty lines ret << "\n"; continue; } ret << line << "\n"; } } } return ret.str(); } std::string Preprocessor::removeParentheses(const std::string &str) { if (str.find("\n#if") == std::string::npos && str.compare(0, 3, "#if") != 0) return str; std::istringstream istr(str); std::ostringstream ret; std::string line; while (std::getline(istr, line)) { if (line.compare(0, 3, "#if") == 0 || line.compare(0, 5, "#elif") == 0) { std::string::size_type pos; pos = 0; while ((pos = line.find(" (", pos)) != std::string::npos) line.erase(pos, 1); pos = 0; while ((pos = line.find("( ", pos)) != std::string::npos) line.erase(pos + 1, 1); pos = 0; while ((pos = line.find(" )", pos)) != std::string::npos) line.erase(pos, 1); pos = 0; while ((pos = line.find(") ", pos)) != std::string::npos) line.erase(pos + 1, 1); // Remove inner parentheses "((..))".. pos = 0; while ((pos = line.find("((", pos)) != std::string::npos) { ++pos; std::string::size_type pos2 = line.find_first_of("()", pos + 1); if (pos2 != std::string::npos && line[pos2] == ')') { line.erase(pos2, 1); line.erase(pos, 1); } } // "#if(A) => #if A", but avoid "#if (defined A) || defined (B)" if ((line.compare(0, 4, "#if(") == 0 || line.compare(0, 6, "#elif(") == 0) && line[line.length() - 1] == ')') { int ind = 0; for (std::string::size_type i = 0; i < line.length(); ++i) { if (line[i] == '(') ++ind; else if (line[i] == ')') { --ind; if (ind == 0) { if (i == line.length() - 1) { line[line.find('(')] = ' '; line.erase(line.length() - 1); } break; } } } } if (line.compare(0, 4, "#if(") == 0) line.insert(3, " "); else if (line.compare(0, 6, "#elif(") == 0) line.insert(5, " "); } ret << line << "\n"; } return ret.str(); } void Preprocessor::removeAsm(std::string &str) { std::string::size_type pos = 0; while ((pos = str.find("#asm\n", pos)) != std::string::npos) { str.replace(pos, 4, "asm("); std::string::size_type pos2 = str.find("#endasm", pos); if (pos2 != std::string::npos) { str.replace(pos2, 7, ");"); pos = pos2; } } } void Preprocessor::preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths) { std::list configs; std::string data; preprocess(istr, data, configs, filename, includePaths); for (std::list::const_iterator it = configs.begin(); it != configs.end(); ++it) { if (_settings && (_settings->userUndefs.find(*it) == _settings->userUndefs.end())) { result[ *it ] = getcode(data, *it, filename); } } } std::string Preprocessor::removeSpaceNearNL(const std::string &str) { std::string tmp; char prev = 0; for (std::size_t i = 0; i < str.size(); i++) { if (str[i] == ' ' && ((i > 0 && prev == '\n') || (i + 1 < str.size() && 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; } std::string Preprocessor::replaceIfDefined(const std::string &str) const { std::string ret(str); std::string::size_type pos; pos = 0; while ((pos = ret.find("#if defined(", pos)) != std::string::npos) { std::string::size_type pos2 = ret.find(")", pos + 9); if (pos2 > ret.length() - 1) break; if (ret[pos2+1] == '\n') { ret.erase(pos2, 1); ret.erase(pos + 3, 9); ret.insert(pos + 3, "def "); } ++pos; if (_settings && _settings->terminated()) return ""; } pos = 0; while ((pos = ret.find("#if !defined(", pos)) != std::string::npos) { std::string::size_type pos2 = ret.find(")", pos + 9); if (pos2 > ret.length() - 1) break; if (ret[pos2+1] == '\n') { ret.erase(pos2, 1); ret.erase(pos + 3, 10); ret.insert(pos + 3, "ndef "); } ++pos; if (_settings && _settings->terminated()) return ""; } pos = 0; while ((pos = ret.find("#elif defined(", pos)) != std::string::npos) { std::string::size_type pos2 = ret.find(")", pos + 9); if (pos2 > ret.length() - 1) break; if (ret[pos2+1] == '\n') { ret.erase(pos2, 1); ret.erase(pos + 6, 8); } ++pos; if (_settings && _settings->terminated()) return ""; } return ret; } void Preprocessor::preprocessWhitespaces(std::string &processedFile) { // Replace all tabs with spaces.. std::replace(processedFile.begin(), processedFile.end(), '\t', ' '); // Remove all indentation.. if (!processedFile.empty() && processedFile[0] == ' ') processedFile.erase(0, processedFile.find_first_not_of(" ")); // 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) { std::string forcedIncludes; if (file0.empty()) file0 = filename; processedFile = read(srcCodeStream, filename); if (_settings) { for (std::list::iterator it = _settings->userIncludes.begin(); it != _settings->userIncludes.end(); ++it) { std::string cur = *it; // try to open file std::ifstream fin; fin.open(cur.c_str()); if (!fin.is_open()) { missingInclude(cur, 1, cur, UserHeader ); continue; } const std::string fileData = read(fin, filename); fin.close(); forcedIncludes = forcedIncludes + "#file \"" + cur + "\"\n" + "#line 1\n" + fileData + "\n" + "#endfile\n" ; } for (std::vector::iterator it = _settings->library.defines.begin(); it != _settings->library.defines.end(); ++it) { forcedIncludes += *it; } } if (!forcedIncludes.empty()) { processedFile = forcedIncludes + "#file \"" + filename + "\"\n" + "#line 1\n" + processedFile + "#endfile\n" ; } // Remove asm(...) removeAsm(processedFile); // Replace "defined A" with "defined(A)" { std::istringstream istr(processedFile); std::ostringstream ostr; std::string line; while (std::getline(istr, line)) { if (line.compare(0, 4, "#if ") == 0 || line.compare(0, 6, "#elif ") == 0) { std::string::size_type pos = 0; while ((pos = line.find(" defined ")) != std::string::npos) { line[pos+8] = '('; pos = line.find_first_of(" |&", pos + 8); if (pos == std::string::npos) line += ")"; else line.insert(pos, ")"); if (_settings && _settings->terminated()) return; } } ostr << line << "\n"; } processedFile = ostr.str(); } std::map defs(getcfgmap(_settings ? _settings->userDefines : emptyString, _settings, filename)); if (_settings && _settings->_maxConfigs == 1U) { std::set pragmaOnce; std::list includes; processedFile = handleIncludes(processedFile, filename, includePaths, defs, pragmaOnce, includes); resultConfigurations = getcfgs(processedFile, filename, defs); } else { handleIncludes(processedFile, filename, includePaths); processedFile = replaceIfDefined(processedFile); // Get all possible configurations.. resultConfigurations = getcfgs(processedFile, filename, defs); // Remove configurations that are disabled by -U handleUndef(resultConfigurations); } } void Preprocessor::handleUndef(std::list &configurations) const { if (_settings && !_settings->userUndefs.empty()) { for (std::list::iterator cfg = configurations.begin(); cfg != configurations.end();) { bool undef = false; for (std::set::const_iterator it = _settings->userUndefs.begin(); it != _settings->userUndefs.end(); ++it) { if (*it == *cfg) undef = true; else if (cfg->compare(0,it->length(),*it)==0 && cfg->find_first_of(";=") == it->length()) undef = true; else if (cfg->find(";" + *it) == std::string::npos) ; else if (cfg->find(";" + *it + ";") != std::string::npos) undef = true; else if (cfg->find(";" + *it + "=") != std::string::npos) undef = true; else if (cfg->find(";" + *it) + it->size() + 1U == cfg->size()) undef = true; } if (undef) configurations.erase(cfg++); else ++cfg; } } } // Get the DEF in this line: "#ifdef DEF" std::string Preprocessor::getdef(std::string line, bool def) { if (line.empty() || line[0] != '#') return ""; // If def is true, the line must start with "#ifdef" if (def && line.compare(0, 7, "#ifdef ") != 0 && line.compare(0, 4, "#if ") != 0 && (line.compare(0, 6, "#elif ") != 0 || line.compare(0, 7, "#elif !") == 0)) { return ""; } // If def is false, the line must start with "#ifndef" if (!def && line.compare(0, 8, "#ifndef ") != 0 && line.compare(0, 7, "#elif !") != 0) { return ""; } // Remove the "#ifdef" or "#ifndef" if (line.compare(0, 12, "#if defined ") == 0) line.erase(0, 11); else if (line.compare(0, 15, "#elif !defined(") == 0) { line.erase(0, 15); std::string::size_type pos = line.find(")"); // if pos == ::npos then another part of the code will complain // about the mismatch if (pos != std::string::npos) line.erase(pos, 1); } else line.erase(0, line.find(" ")); // Remove all spaces. std::string::size_type pos = 0; while ((pos = line.find(" ", pos)) != std::string::npos) { const unsigned char chprev(static_cast((pos > 0) ? line[pos-1] : 0)); const unsigned char chnext(static_cast((pos + 1 < line.length()) ? line[pos+1] : 0)); if ((std::isalnum(chprev) || chprev == '_') && (std::isalnum(chnext) || chnext == '_')) ++pos; else line.erase(pos, 1); } // The remaining string is our result. return line; } /** Simplify variable in variable map. */ static Token *simplifyVarMapExpandValue(Token *tok, const std::map &variables, std::set seenVariables) { // TODO: handle function-macros too. // Prevent infinite recursion.. if (seenVariables.find(tok->str()) != seenVariables.end()) return tok; seenVariables.insert(tok->str()); const std::map::const_iterator it = variables.find(tok->str()); if (it != variables.end()) { TokenList tokenList(nullptr); std::istringstream istr(it->second); if (tokenList.createTokens(istr)) { // expand token list for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (tok2->isName()) { tok2 = simplifyVarMapExpandValue(tok2, variables, seenVariables); } } // insert token list into "parent" token list for (const Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (tok2->previous()) { tok->insertToken(tok2->str()); tok = tok->next(); } else tok->str(tok2->str()); } } } return tok; } /** * Simplifies the variable map. For example if the map contains A=>B, B=>1, then A=>B is simplified to A=>1. * @param [in,out] variables - a map of variable name to variable value. This map will be modified. */ static void simplifyVarMap(std::map &variables) { for (std::map::iterator i = variables.begin(); i != variables.end(); ++i) { TokenList tokenList(nullptr); std::istringstream istr(i->second); if (tokenList.createTokens(istr)) { for (Token *tok = tokenList.front(); tok; tok = tok->next()) { if (tok->isName()) { std::set seenVariables; tok = simplifyVarMapExpandValue(tok, variables, seenVariables); } } std::string str; for (const Token *tok = tokenList.front(); tok; tok = tok->next()) str.append((tok->previous() ? " " : "") + tok->str()); i->second = str; } } } std::list Preprocessor::getcfgs(const std::string &filedata, const std::string &filename, const std::map &defs) { std::list ret; ret.push_back(""); std::list deflist, ndeflist; // constants defined through "#define" in the code.. std::set defines; std::map alldefinesmap(defs); std::stack > includeStack; includeStack.push(std::pair(filename,false)); // How deep into included files are we currently parsing? // 0=>Source file, 1=>Included by source file, 2=>included by header that was included by source file, etc int filelevel = 0; bool includeguard = false; unsigned int linenr = 0; std::istringstream istr(filedata); std::string line; while (std::getline(istr, line)) { ++linenr; if (_settings && _settings->terminated()) return ret; if (_errorLogger) _errorLogger->reportProgress(filename, "Preprocessing (get configurations 1)", 0); if (line.empty()) continue; if (line.compare(0, 6, "#file ") == 0) { includeguard = true; std::string::size_type start=line.find("\""); std::string::size_type end=line.find("\"",start+1); std::string includeFile=line.substr(start+1,end-start-1); bool fileExcluded = false; ++filelevel; if (! _settings) { fileExcluded = false; } else { fileExcluded = _settings->configurationExcluded(includeFile); } includeStack.push(std::pair(includeFile,fileExcluded)); continue; } else if (line == "#endfile") { includeguard = false; includeStack.pop(); if (filelevel > 0) --filelevel; continue; } if (line.compare(0, 8, "#define ") == 0) { bool valid = false; for (std::string::size_type pos = 8; pos < line.size(); ++pos) { char ch = line[pos]; if (ch=='_' || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || (pos>8 && ch>='0' && ch<='9')) { valid = true; continue; } if (ch==' ' || ch=='(') { if (valid) break; } valid = false; break; } if (!valid) line.clear(); else { std::string definestr = line.substr(8); const std::string::size_type spacepos = definestr.find(" "); if (spacepos != std::string::npos) definestr[spacepos] = '='; defines.insert(definestr); const std::string::size_type separatorpos = definestr.find_first_of("=("); if (separatorpos != std::string::npos && definestr[separatorpos] == '=') { const std::string varname(definestr.substr(0, separatorpos)); const std::string value(definestr.substr(separatorpos + 1)); alldefinesmap[varname] = value; } } } if (!line.empty() && line.compare(0, 3, "#if") != 0) includeguard = false; if (line.compare(0, 5, "#line") == 0) continue; if (line.empty() || line[0] != '#') continue; if (includeguard) continue; bool from_negation = false; std::string def = getdef(line, true); if (def.empty()) { def = getdef(line, false); // sub conditionals of ndef blocks need to be // constructed _without_ the negated define if (!def.empty()) from_negation = true; } if (!def.empty()) { int par = 0; for (std::string::size_type pos = 0; pos < def.length(); ++pos) { if (def[pos] == '(') ++par; else if (def[pos] == ')') { --par; if (par < 0) break; } } if (par != 0) { std::ostringstream lineStream; lineStream << __LINE__; ErrorLogger::ErrorMessage errmsg; ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(filename); loc.line = linenr; errmsg._callStack.push_back(loc); errmsg._severity = Severity::fromString("error"); errmsg.setmsg("mismatching number of '(' and ')' in this line: " + def); errmsg._id = "preprocessor" + lineStream.str(); _errorLogger->reportErr(errmsg); ret.clear(); return ret; } // Replace defined constants simplifyCondition(alldefinesmap, def, false); if (! deflist.empty() && line.compare(0, 6, "#elif ") == 0) deflist.pop_back(); // translate A==1 condition to A=1 configuration if (def.find("==") != std::string::npos) { // Check if condition match pattern "%var% == %num%" // %var% std::string::size_type pos = 0; if (std::isalpha((unsigned char)def[pos]) || def[pos] == '_') { ++pos; while (std::isalnum((unsigned char)def[pos]) || def[pos] == '_') ++pos; } // == if (def.compare(pos,2,"==")==0) pos += 2; // %num% if (pos= def.size()) pos = 0; while (pos < def.size() && std::isxdigit((unsigned char)def[pos])) ++pos; } else { while (pos < def.size() && std::isdigit((unsigned char)def[pos])) ++pos; } // Does the condition match the pattern "%var% == %num%"? if (pos == def.size()) { def.erase(def.find("=="),1); } } } deflist.push_back(def); def = ""; for (std::list::const_iterator it = deflist.begin(); it != deflist.end(); ++it) { if (*it == "0") break; if (*it == "1" || *it == "!") continue; // don't add "T;T": // treat two and more similar nested conditions as one if (def != *it) { if (! def.empty()) def += ";"; def += *it; } /* TODO: Fix TestPreprocessor::test7e (#2552) else { std::ostringstream lineStream; lineStream << __LINE__; ErrorLogger::ErrorMessage errmsg; ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(filename); loc.line = linenr; errmsg._callStack.push_back(loc); errmsg._severity = Severity::error; errmsg.setmsg(*it+" is already guaranteed to be defined"); errmsg._id = "preprocessor" + lineStream.str(); _errorLogger->reportErr(errmsg); } */ } if (from_negation) { ndeflist.push_back(deflist.back()); deflist.back() = "!"; } if (std::find(ret.begin(), ret.end(), def) == ret.end()) { if (!includeStack.top().second) { ret.push_back(def); } else { if (_errorLogger && _settings && _settings->debugwarnings) { std::list locationList; const ErrorLogger::ErrorMessage errmsg(locationList, Severity::debug, "Configuration not considered: " + def +" for file:"+includeStack.top().first, "debug", false); _errorLogger->reportErr(errmsg); } } } } else if (line.compare(0, 5, "#else") == 0 && ! deflist.empty()) { if (deflist.back() == "!" && !ndeflist.empty()) { deflist.back() = ndeflist.back(); ndeflist.pop_back(); } else { std::string tempDef((deflist.back() == "1") ? "0" : "1"); deflist.back() = tempDef; } } else if (line.compare(0, 6, "#endif") == 0 && ! deflist.empty()) { if (deflist.back() == "!" && !ndeflist.empty()) ndeflist.pop_back(); deflist.pop_back(); } } // Remove defined constants from ifdef configurations.. std::size_t count = 0; for (std::list::iterator it = ret.begin(); it != ret.end(); ++it) { if (_errorLogger) _errorLogger->reportProgress(filename, "Preprocessing (get configurations 2)", (100 * count++) / ret.size()); std::string cfg(*it); for (std::set::const_iterator it2 = defines.begin(); it2 != defines.end(); ++it2) { std::string::size_type pos = 0; // Get name of define std::string defineName(*it2); if (defineName.find_first_of("=(") != std::string::npos) defineName.erase(defineName.find_first_of("=(")); // Remove ifdef configurations that match the defineName while ((pos = cfg.find(defineName, pos)) != std::string::npos) { const std::string::size_type pos1 = pos; ++pos; if (pos1 > 0 && cfg[pos1-1] != ';') continue; const std::string::size_type pos2 = pos1 + defineName.length(); if (pos2 < cfg.length() && cfg[pos2] != ';') continue; --pos; cfg.erase(pos, defineName.length()); } } if (cfg.length() != it->length()) { while (cfg.length() > 0 && cfg[0] == ';') cfg.erase(0, 1); while (cfg.length() > 0 && cfg[cfg.length()-1] == ';') cfg.erase(cfg.length() - 1); std::string::size_type pos = 0; while ((pos = cfg.find(";;", pos)) != std::string::npos) cfg.erase(pos, 1); *it = cfg; } } // convert configurations: "defined(A) && defined(B)" => "A;B" for (std::list::iterator it = ret.begin(); it != ret.end(); ++it) { std::string s(*it); if (s.find("&&") != std::string::npos) { Tokenizer tokenizer(_settings, _errorLogger); if (!tokenizer.tokenizeCondition(s)) { std::ostringstream lineStream; lineStream << __LINE__; ErrorLogger::ErrorMessage errmsg; ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(filename); loc.line = 1; errmsg._callStack.push_back(loc); errmsg._severity = Severity::error; errmsg.setmsg("Error parsing this: " + s); errmsg._id = "preprocessor" + lineStream.str(); _errorLogger->reportErr(errmsg); } const Token *tok = tokenizer.tokens(); std::set varList; while (tok) { if (Token::Match(tok, "defined ( %var% )")) { varList.insert(tok->strAt(2)); tok = tok->tokAt(4); if (tok && tok->str() == "&&") { tok = tok->next(); } } else if (Token::Match(tok, "%var% ;")) { varList.insert(tok->str()); tok = tok->tokAt(2); } else { break; } } s = join(varList, ';'); if (!s.empty()) *it = s; } } // Convert configurations into a canonical form: B;C;A or C;A;B => A;B;C for (std::list::iterator it = ret.begin(); it != ret.end(); ++it) *it = unify(*it, ';'); // Remove duplicates from the ret list.. ret.sort(); ret.unique(); // cleanup unhandled configurations.. for (std::list::iterator it = ret.begin(); it != ret.end();) { const std::string s(*it + ";"); bool unhandled = false; for (std::string::size_type pos = 0; pos < s.length(); ++pos) { const unsigned char c = static_cast(s[pos]); // ok with ";" if (c == ';') continue; // identifier.. if (std::isalpha(c) || c == '_') { while (std::isalnum((unsigned char)s[pos]) || s[pos] == '_') ++pos; if (s[pos] == '=') { ++pos; while (std::isdigit((unsigned char)s[pos])) ++pos; if (s[pos] != ';') { unhandled = true; break; } } --pos; continue; } // not ok.. else { unhandled = true; break; } } if (unhandled) { // unhandled ifdef configuration.. if (_errorLogger && _settings && _settings->debugwarnings) { std::list locationList; const ErrorLogger::ErrorMessage errmsg(locationList, Severity::debug, "unhandled configuration: " + *it, "debug", false); _errorLogger->reportErr(errmsg); } ret.erase(it++); } else { ++it; } } return ret; } void Preprocessor::simplifyCondition(const std::map &cfg, std::string &condition, bool match) { const Settings settings; Tokenizer tokenizer(&settings, _errorLogger); if (!tokenizer.tokenizeCondition("(" + condition + ")")) { // If tokenize returns false, then there is syntax error in the // code which we can't handle. So stop here. return; } if (Token::Match(tokenizer.tokens(), "( %var% )")) { std::map::const_iterator var = cfg.find(tokenizer.tokens()->strAt(1)); if (var != cfg.end()) { const std::string &value = (*var).second; condition = (value == "0") ? "0" : "1"; } else if (match) condition = "0"; return; } if (Token::Match(tokenizer.tokens(), "( ! %var% )")) { std::map::const_iterator var = cfg.find(tokenizer.tokens()->strAt(2)); if (var == cfg.end()) condition = "1"; else if (var->second == "0") condition = "1"; else if (match) condition = "0"; return; } // replace variable names with values.. for (Token *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { if (!tok->isName()) continue; if (Token::Match(tok, "defined ( %var% )")) { if (cfg.find(tok->strAt(2)) != cfg.end()) tok->str("1"); else if (match) tok->str("0"); else continue; tok->deleteNext(3); continue; } if (Token::Match(tok, "defined %var%")) { if (cfg.find(tok->strAt(1)) != cfg.end()) tok->str("1"); else if (match) tok->str("0"); else continue; tok->deleteNext(); continue; } const std::map::const_iterator it = cfg.find(tok->str()); if (it != cfg.end()) { if (!it->second.empty()) { // Tokenize the value Tokenizer tokenizer2(&settings, _errorLogger); tokenizer2.tokenizeCondition(it->second); // Copy the value tokens std::stack link; for (const Token *tok2 = tokenizer2.tokens(); tok2; tok2 = tok2->next()) { tok->str(tok2->str()); if (Token::Match(tok2,"[{([]")) link.push(tok); else if (!link.empty() && Token::Match(tok2,"[})]]")) { Token::createMutualLinks(link.top(), tok); link.pop(); } if (tok2->next()) { tok->insertToken(""); tok = tok->next(); } } } else if ((!tok->previous() || Token::Match(tok->previous(), "&&|%oror%|(")) && (!tok->next() || Token::Match(tok->next(), "&&|%oror%|)"))) tok->str("1"); else tok->deleteThis(); } } // simplify calculations.. tokenizer.concatenateNegativeNumberAndAnyPositive(); bool modified = true; while (modified) { modified = false; modified |= tokenizer.simplifySizeof(); modified |= tokenizer.simplifyCalculations(); modified |= tokenizer.simplifyConstTernaryOp(); modified |= tokenizer.simplifyRedundantParentheses(); for (Token *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { if (Token::Match(tok, "! %num%")) { tok->deleteThis(); tok->str(tok->str() == "0" ? "1" : "0"); modified = true; } } } for (Token *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { if (Token::Match(tok, "(|%oror%|&& %num% &&|%oror%|)")) { if (tok->next()->str() != "0") { tok->next()->str("1"); } } } for (Token *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { while (Token::Match(tok, "(|%oror% %any% %oror% 1")) { tok->deleteNext(2); if (tok->tokAt(-3)) tok = tok->tokAt(-3); } } if (Token::simpleMatch(tokenizer.tokens(), "( 1 )") || Token::simpleMatch(tokenizer.tokens(), "( 1 ||")) condition = "1"; else if (Token::simpleMatch(tokenizer.tokens(), "( 0 )")) condition = "0"; } bool Preprocessor::match_cfg_def(std::map cfg, std::string def) { /* std::cout << "cfg: \""; for (std::map::const_iterator it = cfg.begin(); it != cfg.end(); ++it) { std::cout << it->first; if (!it->second.empty()) std::cout << "=" << it->second; std::cout << ";"; } std::cout << "\" "; std::cout << "def: \"" << def << "\"\n"; */ simplifyVarMap(cfg); simplifyCondition(cfg, def, true); if (cfg.find(def) != cfg.end()) return true; if (def == "0") return false; if (def == "1") return true; return false; } std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename) { // For the error report unsigned int lineno = 0; std::ostringstream ret; bool match = true; std::list matching_ifdef; std::list matched_ifdef; // Create a map for the cfg for faster access to defines std::map cfgmap(getcfgmap(cfg, _settings, filename)); std::stack filenames; filenames.push(filename); std::stack lineNumbers; std::istringstream istr(filedata); std::string line; while (std::getline(istr, line)) { ++lineno; if (_settings && _settings->terminated()) return ""; if (line.compare(0, 11, "#pragma asm") == 0) { ret << "\n"; bool found_end = false; while (getline(istr, line)) { if (line.compare(0, 14, "#pragma endasm") == 0) { found_end = true; break; } ret << "\n"; } if (!found_end) break; if (line.find("=") != std::string::npos) { Tokenizer tokenizer(_settings, _errorLogger); line.erase(0, sizeof("#pragma endasm")); std::istringstream tempIstr(line); tokenizer.tokenize(tempIstr, "", "", true); if (Token::Match(tokenizer.tokens(), "( %var% = %any% )")) { ret << "asm(" << tokenizer.tokens()->strAt(1) << ");"; } } ret << "\n"; continue; } const std::string def = getdef(line, true); const std::string ndef = getdef(line, false); const bool emptymatch = matching_ifdef.empty() || matched_ifdef.empty(); if (line.compare(0, 8, "#define ") == 0) { match = true; if (_settings) { typedef std::set::const_iterator It; for (It it = _settings->userUndefs.begin(); it != _settings->userUndefs.end(); ++it) { std::string::size_type pos = line.find_first_not_of(' ',8); if (pos != std::string::npos) { std::string::size_type pos2 = line.find(*it,pos); if ((pos2 != std::string::npos) && ((line.size() == pos2 + (*it).size()) || (line[pos2 + (*it).size()] == ' ') || (line[pos2 + (*it).size()] == '('))) { match = false; break; } } } } if (match) { for (std::list::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it) { if (!bool(*it)) { match = false; break; } } } if (match) { std::string::size_type pos = line.find_first_of(" (", 8); if (pos == std::string::npos) cfgmap[line.substr(8)] = ""; else if (line[pos] == ' ') { std::string value(line.substr(pos + 1)); if (cfgmap.find(value) != cfgmap.end()) value = cfgmap[value]; cfgmap[line.substr(8, pos - 8)] = value; } else cfgmap[line.substr(8, pos - 8)] = ""; } } else if (line.compare(0, 7, "#undef ") == 0) { const std::string name(line.substr(7)); cfgmap.erase(name); } else if (!emptymatch && line.compare(0, 7, "#elif !") == 0) { if (matched_ifdef.back()) { matching_ifdef.back() = false; } else { if (!match_cfg_def(cfgmap, ndef)) { matching_ifdef.back() = true; matched_ifdef.back() = true; } } } else if (!emptymatch && line.compare(0, 6, "#elif ") == 0) { if (matched_ifdef.back()) { matching_ifdef.back() = false; } else { if (match_cfg_def(cfgmap, def)) { matching_ifdef.back() = true; matched_ifdef.back() = true; } } } else if (line.compare(0,4,"#if ") == 0) { matching_ifdef.push_back(match_cfg_def(cfgmap, line.substr(4))); matched_ifdef.push_back(matching_ifdef.back()); } else if (! def.empty()) { matching_ifdef.push_back(cfgmap.find(def) != cfgmap.end()); matched_ifdef.push_back(matching_ifdef.back()); } else if (! ndef.empty()) { matching_ifdef.push_back(cfgmap.find(ndef) == cfgmap.end()); matched_ifdef.push_back(matching_ifdef.back()); } else if (!emptymatch && line == "#else") { if (! matched_ifdef.empty()) matching_ifdef.back() = ! matched_ifdef.back(); } else if (line.compare(0, 6, "#endif") == 0) { if (! matched_ifdef.empty()) matched_ifdef.pop_back(); if (! matching_ifdef.empty()) matching_ifdef.pop_back(); } if (!line.empty() && line[0] == '#') { match = true; for (std::list::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it) { if (!bool(*it)) { match = false; break; } } } // #error => return "" if (match && line.compare(0, 6, "#error") == 0) { if (_settings && !_settings->userDefines.empty() && !_settings->_force) { error(filenames.top(), lineno, line); } return ""; } if (!match && (line.compare(0, 8, "#define ") == 0 || line.compare(0, 6, "#undef") == 0)) { // Remove define that is not part of this configuration line = ""; } else if (line.compare(0, 7, "#file \"") == 0 || line.compare(0, 8, "#endfile") == 0 || line.compare(0, 8, "#define ") == 0 || line.compare(0, 6, "#line ") == 0 || line.compare(0, 6, "#undef") == 0) { // We must not remove #file tags or line numbers // are corrupted. File tags are removed by the tokenizer. // Keep location info updated if (line.compare(0, 7, "#file \"") == 0) { filenames.push(line.substr(7, line.size() - 8)); lineNumbers.push(lineno); lineno = 0; } else if (line.compare(0, 8, "#endfile") == 0) { if (filenames.size() > 1U) filenames.pop(); if (!lineNumbers.empty()) { lineno = lineNumbers.top(); lineNumbers.pop(); } } } else if (!match || line.compare(0, 1, "#") == 0) { // Remove #if, #else, #pragma etc, leaving only // #define, #undef, #file and #endfile. and also lines // which are not part of this configuration. line = ""; } ret << line << "\n"; } if (!validateCfg(ret.str(), cfg)) { return ""; } return expandMacros(ret.str(), filename, cfg, _errorLogger); } void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg) { std::list locationList; if (!filename.empty()) { ErrorLogger::ErrorMessage::FileLocation loc; loc.line = linenr; loc.setfile(filename); locationList.push_back(loc); } _errorLogger->reportErr(ErrorLogger::ErrorMessage(locationList, Severity::error, msg, "preprocessorErrorDirective", false)); } Preprocessor::HeaderTypes Preprocessor::getHeaderFileName(std::string &str) { std::string result; std::string::size_type i = str.find_first_of("<\""); if (i == std::string::npos) { str = ""; return NoHeader; } char c = str[i]; if (c == '<') c = '>'; for (i = i + 1; i < str.length(); ++i) { if (str[i] == c) break; result.append(1, str[i]); } // Linux can't open include paths with \ separator, so fix them std::replace(result.begin(), result.end(), '\\', '/'); str = result; return (c == '\"') ? UserHeader : SystemHeader; } /** * Try to open header * @param filename header name (in/out) * @param includePaths paths where to look for the file * @param filePath path to the header file * @param fin file input stream (in/out) * @return if file is opened then true is returned */ static bool openHeader(std::string &filename, const std::list &includePaths, const std::string &filePath, std::ifstream &fin) { fin.open((filePath + filename).c_str()); if (fin.is_open()) { filename = filePath + filename; return true; } std::list includePaths2(includePaths); includePaths2.push_front(""); for (std::list::const_iterator iter = includePaths2.begin(); iter != includePaths2.end(); ++iter) { const std::string nativePath(Path::toNativeSeparators(*iter)); fin.open((nativePath + filename).c_str()); if (fin.is_open()) { filename = nativePath + filename; return true; } fin.clear(); } return false; } std::string Preprocessor::handleIncludes(const std::string &code, const std::string &filePath, const std::list &includePaths, std::map &defs, std::set &pragmaOnce, std::list includes) { const std::string path(filePath.substr(0, 1 + filePath.find_last_of("\\/"))); // current #if indent level. std::stack::size_type indent = 0; // how deep does the #if match? this can never be bigger than "indent". std::stack::size_type indentmatch = 0; // has there been a true #if condition at the current indentmatch level? // then no more #elif or #else can be true before the #endif is seen. std::stack elseIsTrueStack; unsigned int linenr = 0; std::set undefs = _settings ? _settings->userUndefs : std::set(); if (_errorLogger) _errorLogger->reportProgress(filePath, "Preprocessor (handleIncludes)", 0); std::ostringstream ostr; std::istringstream istr(code); std::string line; bool suppressCurrentCodePath = false; while (std::getline(istr,line)) { ++linenr; if (_settings && _settings->terminated()) return ""; // has there been a true #if condition at the current indentmatch level? // then no more #elif or #else can be true before the #endif is seen. while (elseIsTrueStack.size() != indentmatch + 1) { if (elseIsTrueStack.size() < indentmatch + 1) { elseIsTrueStack.push(true); } else { elseIsTrueStack.pop(); } } if (elseIsTrueStack.empty()) { writeError(filePath, linenr, _errorLogger, "syntaxError", "Syntax error in preprocessor code"); return ""; } std::stack::reference elseIsTrue = elseIsTrueStack.top(); if (line == "#pragma once") { pragmaOnce.insert(filePath); } else if (line.compare(0,7,"#ifdef ") == 0) { if (indent == indentmatch) { const std::string tag = getdef(line,true); if (defs.find(tag) != defs.end()) { elseIsTrue = false; indentmatch++; } else if (undefs.find(tag) != undefs.end()) { elseIsTrue = true; indentmatch++; suppressCurrentCodePath = true; } } ++indent; if (indent == indentmatch + 1) elseIsTrue = true; } else if (line.compare(0,8,"#ifndef ") == 0) { if (indent == indentmatch) { const std::string tag = getdef(line,false); if (defs.find(tag) == defs.end()) { elseIsTrue = false; indentmatch++; } else if (undefs.find(tag) != undefs.end()) { elseIsTrue = false; indentmatch++; suppressCurrentCodePath = false; } } ++indent; if (indent == indentmatch + 1) elseIsTrue = true; } else if (line.compare(0,4,"#if ") == 0) { if (!suppressCurrentCodePath && indent == indentmatch && match_cfg_def(defs, line.substr(4))) { elseIsTrue = false; indentmatch++; } ++indent; if (indent == indentmatch + 1) elseIsTrue = true; // this value doesn't matter when suppressCurrentCodePath is true } else if (line.compare(0,6,"#elif ") == 0 || line.compare(0,5,"#else") == 0) { if (!elseIsTrue) { if (indentmatch == indent) { indentmatch = indent - 1; } } else { if (indentmatch == indent) { indentmatch = indent - 1; } else if (indentmatch == indent - 1) { if (line.compare(0,5,"#else")==0 || match_cfg_def(defs,line.substr(6))) { indentmatch = indent; elseIsTrue = false; } } } } else if (line.compare(0, 6, "#endif") == 0) { if (indent > 0) --indent; if (indentmatch > indent || indent == 0) { indentmatch = indent; elseIsTrue = false; suppressCurrentCodePath = false; } } else if (indentmatch == indent) { if (!suppressCurrentCodePath && line.compare(0, 8, "#define ") == 0) { const unsigned int endOfDefine = 8; std::string::size_type endOfTag = line.find_first_of("( ", endOfDefine); std::string tag; // define a symbol if (endOfTag == std::string::npos) { tag = line.substr(endOfDefine); defs[tag] = ""; } else { tag = line.substr(endOfDefine, endOfTag-endOfDefine); // define a function-macro if (line[endOfTag] == '(') { defs[tag] = ""; } // define value else { ++endOfTag; const std::string& value = line.substr(endOfTag, line.size()-endOfTag); if (defs.find(value) != defs.end()) defs[tag] = defs[value]; else defs[tag] = value; } } if (undefs.find(tag) != undefs.end()) { defs.erase(tag); } } else if (!suppressCurrentCodePath && line.compare(0,7,"#undef ") == 0) { defs.erase(line.substr(7)); } else if (!suppressCurrentCodePath && line.compare(0,9,"#include ")==0) { std::string filename(line.substr(9)); const HeaderTypes headerType = getHeaderFileName(filename); if (headerType == NoHeader) { ostr << std::endl; continue; } // try to open file std::string filepath; if (headerType == UserHeader) filepath = path; std::ifstream fin; if (!openHeader(filename, includePaths, filepath, fin)) { missingInclude(Path::toNativeSeparators(filePath), linenr, filename, headerType ); ostr << std::endl; continue; } // Prevent that files are recursively included if (std::find(includes.begin(), includes.end(), filename) != includes.end()) { ostr << std::endl; continue; } includes.push_back(filename); // Don't include header if it's already included and contains #pragma once if (pragmaOnce.find(filename) != pragmaOnce.end()) { ostr << std::endl; continue; } ostr << "#file \"" << filename << "\"\n" << handleIncludes(read(fin, filename), filename, includePaths, defs, pragmaOnce, includes) << std::endl << "#endfile\n"; continue; } if (!suppressCurrentCodePath) ostr << line; } // A line has been read.. ostr << "\n"; } return ostr.str(); } void Preprocessor::handleIncludes(std::string &code, const std::string &filePath, const std::list &includePaths) { std::list paths; std::string path; path = filePath; path.erase(1 + path.find_last_of("\\/")); paths.push_back(path); std::string::size_type pos = 0; std::string::size_type endfilePos = 0; std::set handledFiles; while ((pos = code.find("#include", pos)) != std::string::npos) { if (_settings && _settings->terminated()) return; // Accept only includes that are at the start of a line if (pos > 0 && code[pos-1] != '\n') { pos += 8; // length of "#include" continue; } // If endfile is encountered, we have moved to a next file in our stack, // so remove last path in our list. while (!paths.empty() && (endfilePos = code.find("\n#endfile", endfilePos)) != std::string::npos && endfilePos < pos) { paths.pop_back(); endfilePos += 9; // size of #endfile } endfilePos = pos; std::string::size_type end = code.find("\n", pos); std::string filename = code.substr(pos, end - pos); // Remove #include clause code.erase(pos, end - pos); HeaderTypes headerType = getHeaderFileName(filename); if (headerType == NoHeader) continue; // filename contains now a file name e.g. "menu.h" std::string processedFile; std::string filepath; if (headerType == UserHeader && !paths.empty()) filepath = paths.back(); std::ifstream fin; const bool fileOpened(openHeader(filename, includePaths, filepath, fin)); if (fileOpened) { filename = Path::simplifyPath(filename); std::string tempFile = filename; std::transform(tempFile.begin(), tempFile.end(), tempFile.begin(), tolowerWrapper); if (handledFiles.find(tempFile) != handledFiles.end()) { // We have processed this file already once, skip // it this time to avoid eternal loop. fin.close(); continue; } handledFiles.insert(tempFile); processedFile = Preprocessor::read(fin, filename); fin.close(); } if (!processedFile.empty()) { // Remove space characters that are after or before new line character processedFile = "#file \"" + Path::fromNativeSeparators(filename) + "\"\n" + processedFile + "\n#endfile"; code.insert(pos, processedFile); path = filename; path.erase(1 + path.find_last_of("\\/")); paths.push_back(path); } else if (!fileOpened && _settings) { std::string f = filePath; // Determine line number of include unsigned int linenr = 1; unsigned int level = 0; for (std::string::size_type p = 1; p <= pos; ++p) { if (level == 0 && code[pos-p] == '\n') ++linenr; else if (code.compare(pos-p, 9, "#endfile\n") == 0) { ++level; } else if (code.compare(pos-p, 6, "#file ") == 0) { if (level == 0) { linenr--; const std::string::size_type pos1 = pos - p + 7; const std::string::size_type pos2 = code.find_first_of("\"\n", pos1); f = code.substr(pos1, (pos2 == std::string::npos) ? pos2 : (pos2 - pos1)); break; } --level; } } missingInclude(Path::toNativeSeparators(f), linenr, filename, headerType); } } } // 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, 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); errmsg.file0 = file0; _errorLogger->reportInfo(errmsg); } } /** * Skip string in line. A string begins and ends with either a " or a ' * @param line the string * @param pos in=start position of string, out=end position of string */ static void skipstring(const std::string &line, std::string::size_type &pos) { const char ch = line[pos]; ++pos; while (pos < line.size() && line[pos] != ch) { if (line[pos] == '\\') ++pos; ++pos; } } /** * Remove heading and trailing whitespaces from the input parameter. * @param s The string to trim. */ static std::string trim(const std::string& s) { std::string::size_type beg = s.find_first_not_of(" \t"); if (beg == std::string::npos) return s; std::string::size_type end = s.find_last_not_of(" \t"); if (end == std::string::npos) return s.substr(beg); return s.substr(beg, end - beg + 1); } /** * @brief get parameters from code. For example 'foo(1,2)' => '1','2' * @param line in: The code * @param pos in: Position to the '('. out: Position to the ')' * @param params out: The extracted parameters * @param numberOfNewlines out: number of newlines in the macro call * @param endFound out: was the end parentheses found? */ static void getparams(const std::string &line, std::string::size_type &pos, std::vector ¶ms, unsigned int &numberOfNewlines, bool &endFound) { params.clear(); numberOfNewlines = 0; endFound = false; if (line[pos] == ' ') pos++; if (line[pos] != '(') return; // parentheses level int parlevel = 0; // current parameter data std::string par; // scan for parameters.. for (; pos < line.length(); ++pos) { // increase parentheses level if (line[pos] == '(') { ++parlevel; if (parlevel == 1) continue; } // decrease parentheses level else if (line[pos] == ')') { --parlevel; if (parlevel <= 0) { endFound = true; params.push_back(trim(par)); break; } } // string else if (line[pos] == '\"' || line[pos] == '\'') { const std::string::size_type p = pos; skipstring(line, pos); if (pos == line.length()) break; par += line.substr(p, pos + 1 - p); continue; } // count newlines. the expanded macro must have the same number of newlines else if (line[pos] == '\n') { ++numberOfNewlines; continue; } // new parameter if (parlevel == 1 && line[pos] == ',') { params.push_back(trim(par)); par = ""; } // spaces are only added if needed else if (line[pos] == ' ') { // Add space only if it is needed if (par.size() && std::isalnum((unsigned char)par[par.length()-1])) { par += ' '; } } // add character to current parameter else if (parlevel >= 1 && line[pos] != Preprocessor::macroChar) { par.append(1, line[pos]); } } } /** @brief Class that the preprocessor uses when it expands macros. This class represents a preprocessor macro */ class PreprocessorMacro { private: Settings settings; /** tokens of this macro */ Tokenizer tokenizer; /** macro parameters */ std::vector _params; /** name of macro */ std::string _name; /** macro definition in plain text */ const std::string _macro; /** prefix that is used by cppcheck to separate macro parameters. Always "__cppcheck__" */ const std::string _prefix; /** does this macro take a variable number of parameters? */ bool _variadic; /** The macro has parentheses but no parameters.. "AAA()" */ bool _nopar; /** disabled assignment operator */ void operator=(const PreprocessorMacro &); /** @brief expand inner macro */ std::vector expandInnerMacros(const std::vector ¶ms1, const std::map ¯os) const { std::string innerMacroName; // Is there an inner macro.. { const Token *tok = Token::findsimplematch(tokens(), ")"); if (!Token::Match(tok, ") %var% (")) return params1; innerMacroName = tok->strAt(1); tok = tok->tokAt(3); unsigned int par = 0; while (Token::Match(tok, "%var% ,|)")) { tok = tok->tokAt(2); par++; } if (tok || par != params1.size()) return params1; } std::vector params2(params1); for (std::size_t ipar = 0; ipar < params1.size(); ++ipar) { const std::string s(innerMacroName + "("); std::string param(params1[ipar]); if (param.compare(0,s.length(),s)==0 && param[param.length()-1]==')') { std::vector innerparams; std::string::size_type pos = s.length() - 1; unsigned int num = 0; bool endFound = false; getparams(param, pos, innerparams, num, endFound); if (pos == param.length()-1 && num==0 && endFound && innerparams.size() == params1.size()) { // Is inner macro defined? std::map::const_iterator it = macros.find(innerMacroName); if (it != macros.end()) { // expand the inner macro const PreprocessorMacro *innerMacro = it->second; std::string innercode; std::map innermacros = macros; innermacros.erase(innerMacroName); innerMacro->code(innerparams, innermacros, innercode); params2[ipar] = innercode; } } } } return params2; } public: /** * @brief Constructor for PreprocessorMacro. This is the "setter" * for this class - everything is setup here. * @param macro The code after define, until end of line, * e.g. "A(x) foo(x);" */ explicit PreprocessorMacro(const std::string ¯o) : _macro(macro), _prefix("__cppcheck__") { tokenizer.setSettings(&settings); // Tokenize the macro to make it easier to handle std::istringstream istr(macro); tokenizer.list.createTokens(istr); // macro name.. if (tokens() && tokens()->isName()) _name = tokens()->str(); // initialize parameters to default values _variadic = _nopar = false; std::string::size_type pos = macro.find_first_of(" ("); if (pos != std::string::npos && macro[pos] == '(') { // Extract macro parameters if (Token::Match(tokens(), "%var% ( %var%")) { for (const Token *tok = tokens()->tokAt(2); tok; tok = tok->next()) { if (tok->str() == ")") break; if (Token::simpleMatch(tok, ". . . )")) { if (tok->previous()->str() == ",") _params.push_back("__VA_ARGS__"); _variadic = true; break; } if (tok->isName()) _params.push_back(tok->str()); } } else if (Token::Match(tokens(), "%var% ( . . . )")) _variadic = true; else if (Token::Match(tokens(), "%var% ( )")) _nopar = true; } } /** return tokens of this macro */ const Token *tokens() const { return tokenizer.tokens(); } /** read parameters of this macro */ const std::vector ¶ms() const { return _params; } /** check if this is macro has a variable number of parameters */ bool variadic() const { return _variadic; } /** Check if this macro has parentheses but no parameters */ bool nopar() const { return _nopar; } /** name of macro */ const std::string &name() const { return _name; } /** * get expanded code for this macro * @param params2 macro parameters * @param macros macro definitions (recursion) * @param macrocode output string * @return true if the expanding was successful */ bool code(const std::vector ¶ms2, const std::map ¯os, std::string ¯ocode) const { if (_nopar || (_params.empty() && _variadic)) { macrocode = _macro.substr(1 + _macro.find(")")); if (macrocode.empty()) return true; std::string::size_type pos = 0; // Remove leading spaces if ((pos = macrocode.find_first_not_of(" ")) > 0) macrocode.erase(0, pos); // Remove ending newline if ((pos = macrocode.find_first_of("\r\n")) != std::string::npos) macrocode.erase(pos); // Replace "__VA_ARGS__" with parameters if (!_nopar) { std::string s; for (std::size_t i = 0; i < params2.size(); ++i) { if (i > 0) s += ","; s += params2[i]; } pos = 0; while ((pos = macrocode.find("__VA_ARGS__", pos)) != std::string::npos) { macrocode.erase(pos, 11); macrocode.insert(pos, s); pos += s.length(); } } } else if (_params.empty()) { std::string::size_type pos = _macro.find_first_of(" \""); if (pos == std::string::npos) macrocode = ""; else { if (_macro[pos] == ' ') pos++; macrocode = _macro.substr(pos); if ((pos = macrocode.find_first_of("\r\n")) != std::string::npos) macrocode.erase(pos); } } else { const std::vector givenparams = expandInnerMacros(params2, macros); const Token *tok = tokens(); while (tok && tok->str() != ")") tok = tok->next(); if (tok) { bool optcomma = false; while (nullptr != (tok = tok->next())) { std::string str = tok->str(); if (str == "##") continue; if (str[0] == '#' || tok->isName()) { const bool stringify(str[0] == '#'); if (stringify) { str = str.erase(0, 1); } for (std::size_t i = 0; i < _params.size(); ++i) { if (str == _params[i]) { if (_variadic && (i == _params.size() - 1 || (givenparams.size() + 2 == _params.size() && i + 1 == _params.size() - 1))) { str = ""; for (std::size_t j = _params.size() - 1; j < givenparams.size(); ++j) { if (optcomma || j > _params.size() - 1) str += ","; optcomma = false; str += givenparams[j]; } } else if (i >= givenparams.size()) { // Macro had more parameters than caller used. macrocode = ""; return false; } else if (stringify) { const std::string &s(givenparams[i]); std::ostringstream ostr; ostr << "\""; for (std::string::size_type j = 0; j < s.size(); ++j) { if (s[j] == '\\' || s[j] == '\"') ostr << '\\'; ostr << s[j]; } str = ostr.str() + "\""; } else str = givenparams[i]; break; } } // expand nopar macro if (tok->strAt(-1) != "##") { const std::map::const_iterator it = macros.find(str); if (it != macros.end() && it->second->_macro.find("(") == std::string::npos) { str = it->second->_macro; if (str.find(" ") != std::string::npos) str.erase(0, str.find(" ")); else str = ""; } } } if (_variadic && tok->str() == "," && tok->next() && tok->next()->str() == "##") { optcomma = true; continue; } optcomma = false; macrocode += str; if (Token::Match(tok, "%var% %var%") || Token::Match(tok, "%var% %num%") || Token::Match(tok, "%num% %var%") || Token::simpleMatch(tok, "> >")) macrocode += " "; } } } return true; } }; /** * Get data from a input string. This is an extended version of std::getline. * The std::getline only get a single line at a time. It can therefore happen that it * contains a partial statement. This function ensures that the returned data * doesn't end in the middle of a statement. The "getlines" name indicate that * this function will return multiple lines if needed. * @param istr input stream * @param line output data * @return success */ static bool getlines(std::istream &istr, std::string &line) { if (!istr.good()) return false; line = ""; int parlevel = 0; for (char ch = (char)istr.get(); istr.good(); ch = (char)istr.get()) { if (ch == '\'' || ch == '\"') { line += ch; char c = 0; while (istr.good() && c != ch) { if (c == '\\') { c = (char)istr.get(); if (!istr.good()) return true; line += c; } c = (char)istr.get(); if (!istr.good()) return true; if (c == '\n' && line.compare(0, 1, "#") == 0) return true; line += c; } continue; } if (ch == '(') ++parlevel; else if (ch == ')') --parlevel; else if (ch == '\n') { if (line.compare(0, 1, "#") == 0) return true; if (istr.peek() == '#') { line += ch; return true; } } else if (line.compare(0, 1, "#") != 0 && parlevel <= 0 && ch == ';') { line += ";"; return true; } line += ch; } return true; } bool Preprocessor::validateCfg(const std::string &code, const std::string &cfg) { // fill up "macros" with empty configuration macros std::set macros; for (std::string::size_type pos = 0; pos < cfg.size();) { const std::string::size_type pos2 = cfg.find_first_of(";=", pos); if (pos2 == std::string::npos) { macros.insert(cfg.substr(pos)); break; } if (cfg[pos2] == ';') macros.insert(cfg.substr(pos, pos2-pos)); pos = cfg.find(";", pos2); if (pos != std::string::npos) ++pos; } // check if any empty macros are used in code for (std::set::const_iterator it = macros.begin(); it != macros.end(); ++it) { const std::string ¯o = *it; std::string::size_type pos = 0; while ((pos = code.find_first_of(std::string("#\"'")+macro[0], pos)) != std::string::npos) { const std::string::size_type pos1 = pos; const std::string::size_type pos2 = pos + macro.size(); pos++; // skip string.. if (code[pos1] == '\"' || code[pos1] == '\'') { while (pos < code.size() && code[pos] != code[pos1]) { if (code[pos] == '\\') ++pos; ++pos; } ++pos; } // skip preprocessor statement.. else if (code[pos1] == '#') { if (pos1 == 0 || code[pos1-1] == '\n') pos = code.find("\n",pos); } // is macro used in code? else if (code.compare(pos1,macro.size(),macro) == 0) { if (pos1 > 0 && (std::isalnum((unsigned char)code[pos1-1U]) || code[pos1-1U] == '_')) continue; if (pos2 < code.size() && (std::isalnum((unsigned char)code[pos2]) || code[pos2] == '_')) continue; // macro is used in code, return false if (_settings->isEnabled("information")) validateCfgError(cfg, macro); return false; } } } return true; } void Preprocessor::validateCfgError(const std::string &cfg, const std::string ¯o) { const std::string id = "ConfigurationNotChecked"; std::list locationList; ErrorLogger::ErrorMessage::FileLocation loc; loc.line = 1; loc.setfile(file0); locationList.push_back(loc); ErrorLogger::ErrorMessage errmsg(locationList, 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); } std::string Preprocessor::expandMacros(const std::string &code, std::string filename, const std::string &cfg, ErrorLogger *errorLogger) { // Search for macros and expand them.. // -------------------------------------------- // Available macros (key=macroname, value=macro). std::map macros; { // fill up "macros" with user defined macros const std::map cfgmap(getcfgmap(cfg,nullptr,"")); std::map::const_iterator it; for (it = cfgmap.begin(); it != cfgmap.end(); ++it) { std::string s = it->first; if (!it->second.empty()) s += " " + it->second; PreprocessorMacro *macro = new PreprocessorMacro(s); macros[it->first] = macro; } } // Current line number unsigned int linenr = 1; // linenr, filename std::stack< std::pair > fileinfo; // output stream std::ostringstream ostr; // read code.. std::istringstream istr(code); std::string line; while (getlines(istr, line)) { // defining a macro.. if (line.compare(0, 8, "#define ") == 0) { PreprocessorMacro *macro = new PreprocessorMacro(line.substr(8)); if (macro->name().empty() || macro->name() == "NULL") { delete macro; } else if (macro->name() == "BOOST_FOREACH") { // BOOST_FOREACH is currently too complex to parse, so skip it. delete macro; } else { std::map::iterator it; it = macros.find(macro->name()); if (it != macros.end()) delete it->second; macros[macro->name()] = macro; } line = "\n"; } // undefining a macro.. else if (line.compare(0, 7, "#undef ") == 0) { std::map::iterator it; it = macros.find(line.substr(7)); if (it != macros.end()) { delete it->second; macros.erase(it); } line = "\n"; } // entering a file, update position.. else if (line.compare(0, 7, "#file \"") == 0) { fileinfo.push(std::pair(linenr, filename)); filename = line.substr(7, line.length() - 8); linenr = 0; line += "\n"; } // leaving a file, update position.. else if (line == "#endfile") { if (!fileinfo.empty()) { linenr = fileinfo.top().first; filename = fileinfo.top().second; fileinfo.pop(); } line += "\n"; } // all other preprocessor directives are just replaced with a newline else if (line.compare(0, 1, "#") == 0) { line += "\n"; } // expand macros.. else { // Limit for each macro. // The limit specify a position in the "line" variable. // For a "recursive macro" where the expanded text contains // the macro again, the macro should not be expanded again. // The limits are used to prevent recursive expanding. // * When a macro is expanded its limit position is set to // the last expanded character. // * macros are only allowed to be expanded when the // the position is beyond the limit. // * The limit is relative to the end of the "line" // variable. Inserting and deleting text before the limit // without updating the limit is safe. // * when pos goes beyond a limit the limit needs to be // deleted because it is unsafe to insert/delete text // after the limit otherwise std::map limits; // pos is the current position in line std::string::size_type pos = 0; // scan line to see if there are any macros to expand.. unsigned int tmpLinenr = 0; while (pos < line.size()) { if (line[pos] == '\n') ++tmpLinenr; // skip strings.. if (line[pos] == '\"' || line[pos] == '\'') { const char ch = line[pos]; skipstring(line, pos); ++pos; if (pos >= line.size()) { writeError(filename, linenr + tmpLinenr, errorLogger, "noQuoteCharPair", std::string("No pair for character (") + ch + "). Can't process file. File is either invalid or unicode, which is currently not supported."); std::map::iterator it; for (it = macros.begin(); it != macros.end(); ++it) delete it->second; macros.clear(); return ""; } continue; } if (!std::isalpha((unsigned char)line[pos]) && line[pos] != '_') ++pos; // found an identifier.. // the "while" is used in case the expanded macro will immediately call another macro while (pos < line.length() && (std::isalpha((unsigned char)line[pos]) || line[pos] == '_')) { // pos1 = start position of macro const std::string::size_type pos1 = pos++; // find the end of the identifier while (pos < line.size() && (std::isalnum((unsigned char)line[pos]) || line[pos] == '_')) ++pos; // get identifier const std::string id = line.substr(pos1, pos - pos1); // is there a macro with this name? std::map::const_iterator it; it = macros.find(id); if (it == macros.end()) break; // no macro with this name exist const PreprocessorMacro * const macro = it->second; // check that pos is within allowed limits for this // macro { const std::map::const_iterator it2 = limits.find(macro); if (it2 != limits.end() && pos <= line.length() - it2->second) break; } // get parameters from line.. std::vector params; std::string::size_type pos2 = pos; if (macro->params().size() && pos2 >= line.length()) break; // number of newlines within macro use unsigned int numberOfNewlines = 0; // if the macro has parentheses, get parameters if (macro->variadic() || macro->nopar() || macro->params().size()) { // is the end parentheses found? bool endFound = false; getparams(line,pos2,params,numberOfNewlines,endFound); // something went wrong so bail out if (!endFound) break; } // Just an empty parameter => clear if (params.size() == 1 && params[0] == "") params.clear(); // Check that it's the same number of parameters.. if (!macro->variadic() && params.size() != macro->params().size()) break; // Create macro code.. std::string tempMacro; if (!macro->code(params, macros, tempMacro)) { // Syntax error in code writeError(filename, linenr + tmpLinenr, errorLogger, "syntaxError", std::string("Syntax error. Not enough parameters for macro '") + macro->name() + "'."); std::map::iterator iter; for (iter = macros.begin(); iter != macros.end(); ++iter) delete iter->second; macros.clear(); return ""; } // make sure number of newlines remain the same.. std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro); // Insert macro code.. if (macro->variadic() || macro->nopar() || !macro->params().empty()) ++pos2; // Remove old limits for (std::map::iterator iter = limits.begin(); iter != limits.end();) { if ((line.length() - pos1) < iter->second) { // We have gone past this limit, so just delete it limits.erase(iter++); } else { ++iter; } } // don't allow this macro to be expanded again before pos2 limits[macro] = line.length() - pos2; // erase macro line.erase(pos1, pos2 - pos1); // Don't glue this macro into variable or number after it if (!line.empty() && (std::isalnum((unsigned char)line[pos1]) || line[pos1] == '_')) macrocode.append(1,' '); // insert macrochar before each symbol/nr/operator bool str = false; bool chr = false; for (std::size_t i = 0U; i < macrocode.size(); ++i) { if (macrocode[i] == '\\') { i++; continue; } else if (macrocode[i] == '\"') str = !str; else if (macrocode[i] == '\'') chr = !chr; else if (str || chr) continue; else if (macrocode[i] == '.') { // 5. / .5 if ((i > 0U && std::isdigit((unsigned char)macrocode[i-1])) || (i+1 < macrocode.size() && std::isdigit((unsigned char)macrocode[i+1]))) { if (i > 0U && !std::isdigit((unsigned char)macrocode[i-1])) { macrocode.insert(i, 1U, macroChar); i++; } i++; if (i 0U) && (!std::isalnum((unsigned char)macrocode[i-1])) && (macrocode[i-1] != '_') && (macrocode[i-1] != macroChar)) { macrocode.insert(i, 1U, macroChar); } // 1e-7 / 1e+7 if (i+3U < macrocode.size() && (std::isdigit((unsigned char)macrocode[i]) || macrocode[i]=='.') && (macrocode[i+1] == 'e' || macrocode[i+1] == 'E') && (macrocode[i+2] == '-' || macrocode[i+2] == '+') && std::isdigit((unsigned char)macrocode[i+3])) { i += 3U; } // 1.f / 1.e7 if (i+2U < macrocode.size() && std::isdigit((unsigned char)macrocode[i]) && macrocode[i+1] == '.' && std::isalpha((unsigned char)macrocode[i+2])) { i += 2U; if (i+2U < macrocode.size() && (macrocode[i+0] == 'e' || macrocode[i+0] == 'E') && (macrocode[i+1] == '-' || macrocode[i+1] == '+') && std::isdigit((unsigned char)macrocode[i+2])) { i += 2U; } } } } line.insert(pos1, macroChar + macrocode); // position = start position. pos = pos1; } } } // the line has been processed in various ways. Now add it to the output stream ostr << line; // update linenr for (std::string::size_type p = 0; p < line.length(); ++p) { if (line[p] == '\n') ++linenr; } } for (std::map::iterator it = macros.begin(); it != macros.end(); ++it) delete it->second; macros.clear(); return ostr.str(); } void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) { Settings settings2(*settings); Preprocessor preprocessor(&settings2, errorLogger); settings2.checkConfiguration=true; preprocessor.missingInclude("", 1, "", UserHeader); preprocessor.missingInclude("", 1, "", SystemHeader); preprocessor.validateCfgError("X", "X"); preprocessor.error("", 1, "#error message"); // #error .. } cppcheck-1.66/lib/preprocessor.h000066400000000000000000000265371236713773000167210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include #include #include "config.h" class ErrorLogger; class Settings; /// @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; Preprocessor(Settings *settings = nullptr, ErrorLogger *errorLogger = nullptr); static bool missingIncludeFlag; static bool missingSystemIncludeFlag; /** * 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); /** Just read the code into a string. Perform simple cleanup of the code */ std::string read(std::istream &istr, const std::string &filename); /** read preprocessor statements into a string. */ static std::string readpreprocessor(std::istream &istr, const unsigned int bom); /** should __cplusplus be defined? */ static bool cplusplus(const Settings *settings, const std::string &filename); /** * 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); /** * simplify condition * @param variables Variable values * @param condition The condition to simplify * @param match if true, 'defined(A)' is replaced with 0 if A is not defined */ void simplifyCondition(const std::map &variables, std::string &condition, bool match); /** * 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 code The input code * @param cfg configuration * @return true => configuration is valid */ bool validateCfg(const std::string &code, const std::string &cfg); void validateCfgError(const std::string &cfg, const std::string ¯o); void handleUndef(std::list &configurations) const; /** * report error * @param fileName name of file that the error was found in * @param linenr linenr in file * @param errorLogger Error logger to write error to * @param errorType id string for error * @param errorText Plain text */ static void writeError(const std::string &fileName, const unsigned int linenr, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText); /** * Replace "#if defined" with "#ifdef" where possible * * @param str The string to be converted * @return The replaced string */ std::string replaceIfDefined(const std::string &str) const; /** * expand macros in code. ifdefs etc are ignored so the code must be a single configuration * @param code The input code * @param filename filename of source file * @param cfg user given -D configuration * @param errorLogger Error logger to write errors to (if any) * @return the expanded string */ static std::string expandMacros(const std::string &code, std::string filename, const std::string &cfg, ErrorLogger *errorLogger); /** * Remove comments from code. This should only be called from read(). * If there are inline suppressions, the _settings member is modified * @param str Code processed by read(). * @param filename filename * @return code without comments */ std::string removeComments(const std::string &str, const std::string &filename); /** * Cleanup 'if 0' from the code * @param code Code processed by read(). * @return code without 'if 0' */ static std::string removeIf0(const std::string &code); /** * Remove redundant parentheses from preprocessor commands. This should only be called from read(). * @param str Code processed by read(). * @return code with reduced parentheses */ static std::string removeParentheses(const std::string &str); /** * clean up #-preprocessor lines (only) * @param processedFile The data to be processed */ static std::string preprocessCleanupDirectives(const std::string &processedFile); /** * Returns the string between double quote characters or \< \> characters. * @param str e.g. \code#include "menu.h"\endcode or \code#include \endcode * After function call it will contain e.g. "menu.h" without double quotes. * @return NoHeader empty string if double quotes or \< \> were not found. * UserHeader if file surrounded with "" was found * SystemHeader if file surrounded with \<\> was found */ static Preprocessor::HeaderTypes getHeaderFileName(std::string &str); private: /** * 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); static std::string getdef(std::string line, bool def); public: /** * Get all possible configurations sorted in alphabetical order. * By looking at the ifdefs and ifndefs in filedata */ std::list getcfgs(const std::string &filedata, const std::string &filename, const std::map &defs); /** * Remove asm(...) from a string * @param str Code */ static void removeAsm(std::string &str); /** * Evaluate condition 'numerically' * @param cfg configuration * @param def condition * @return result when evaluating the condition */ bool match_cfg_def(std::map cfg, std::string def); static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings); /** * handle includes for a specific configuration * @param code code in string * @param filePath filename of code * @param includePaths Paths where headers might be * @param defs defines (only values) * @param pragmaOnce includes that has already been included and contains a \#pragma once statement * @param includes provide a empty list. this is just used to prevent recursive inclusions. * \return resulting string */ std::string handleIncludes(const std::string &code, const std::string &filePath, const std::list &includePaths, std::map &defs, std::set &pragmaOnce, std::list includes); void setFile0(const std::string &f) { file0 = f; } bool foundUnhandledChars() const { return _foundUnhandledChars; } 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); /** * Search includes from code and append code from the included * file * @param[in,out] code The source code to modify * @param filePath Relative path to 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 handleIncludes(std::string &code, const std::string &filePath, const std::list &includePaths); Settings *_settings; ErrorLogger *_errorLogger; /** filename for cpp/c file - useful when reporting errors */ std::string file0; /** set to true if unhandled chars are found in code. any char is ok * in comments and string literals, but variable/type names must * have plain ascii characters. */ bool _foundUnhandledChars; }; /// @} //--------------------------------------------------------------------------- #endif // preprocessorH cppcheck-1.66/lib/settings.cpp000066400000000000000000000142211236713773000163510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "settings.h" #include "path.h" #include "preprocessor.h" // Preprocessor #include #include Settings::Settings() : _terminate(false), debug(false), debugwarnings(false), debugFalsePositive(false), dump(false), exceptionHandling(false), inconclusive(false), experimental(false), _errorsOnly(false), _inlineSuppressions(false), _verbose(false), _force(false), _relativePaths(false), _xml(false), _xml_version(1), _jobs(1), _loadAverage(0), _exitCode(0), _showtime(SHOWTIME_NONE), _maxConfigs(12), enforcedLang(None), reportProgress(false), checkConfiguration(false), checkLibrary(false) { // 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(Unspecified); #endif } 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)); } bool handled = false; static std::set id; if (id.empty()) { id.insert("warning"); id.insert("style"); id.insert("performance"); id.insert("portability"); id.insert("information"); id.insert("missingInclude"); id.insert("unusedFunction"); #ifdef CHECK_INTERNAL id.insert("internal"); #endif } if (str == "all") { std::set::const_iterator it; for (it = id.begin(); it != id.end(); ++it) { if (*it == "internal") continue; _enabled.insert(*it); } } else if (id.find(str) != id.end()) { _enabled.insert(str); if (str == "information") { _enabled.insert("missingInclude"); } } else if (!handled) { 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::append(const std::string &filename) { std::ifstream fin(filename.c_str()); if (!fin.is_open()) { return false; } std::string line; while (std::getline(fin, line)) { _append += line + "\n"; } Preprocessor::preprocessWhitespaces(_append); return true; } const std::string &Settings::append() const { return _append; } bool Settings::platform(PlatformType type) { switch (type) { case Unspecified: // 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 *); 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; 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; 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; 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; return true; } // unsupported platform return false; } bool Settings::platformFile(const std::string &filename) { (void)filename; /** @todo TBD */ return false; } cppcheck-1.66/lib/settings.h000066400000000000000000000175361236713773000160320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include #include "config.h" #include "library.h" #include "suppressions.h" #include "standards.h" #include "timer.h" /// @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 { private: /** @brief Code to append in the checks */ std::string _append; /** @brief enable extra checks by id */ std::set _enabled; /** @brief terminate checking */ bool _terminate; public: Settings(); /** @brief Is --debug given? */ bool debug; /** @brief Is --debug-warnings given? */ bool debugwarnings; /** @brief Is --debug-fp given? */ bool debugFalsePositive; /** @brief Is --dump given? */ bool dump; /** @brief Is --exception-handling given */ bool exceptionHandling; /** @brief Inconclusive checks */ bool inconclusive; /** * 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 _errorsOnly; /** @brief Is --inline-suppr given? */ bool _inlineSuppressions; /** @brief Is --verbose given? */ bool _verbose; /** @brief Request termination of checking */ void terminate() { _terminate = true; } /** @brief termination requested? */ bool terminated() const { return _terminate; } /** @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 XML results (--xml) */ bool _xml; /** @brief XML version (--xmlver=..) */ 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 List of include paths, e.g. "my/includes/" which should be used for finding include files inside source files. (-I) */ std::list _includePaths; /** @brief assign append code (--append) */ bool append(const std::string &filename); /** @brief get append code (--append) */ const std::string &append() const; /** @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 str id for the extra check, e.g. "style" * @return true if the check is enabled. */ template bool isEnabled(T&& str) const { return bool(_enabled.find(str) != _enabled.end()); } /** * @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); 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("style") { // default severity } std::string tokenlist; std::string pattern; std::string id; std::string severity; std::string summary; }; /** * @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; /** 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; enum PlatformType { Unspecified, // whatever system this code was compiled on Win32A, Win32W, Win64, Unix32, Unix64 }; /** 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; } /** * @brief return true if a file is to be excluded from configuration checking * @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.66/lib/standards.h000066400000000000000000000030321236713773000161370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Marek Zmysłowski and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 //--------------------------------------------------------------------------- /// @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 } c; /** C++ code standard */ enum cppstd_t { CPP03, CPP11 } cpp; /** Code is posix */ bool posix; /** This constructor clear all the variables **/ Standards() : c(C11), cpp(CPP11), posix(false) {} }; /// @} //--------------------------------------------------------------------------- #endif // standardsH cppcheck-1.66/lib/suppressions.cpp000066400000000000000000000247661236713773000173050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "settings.h" #include "path.h" #include #include #include #include // std::isdigit, std::isalnum, etc 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(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 != "*") { // Support "stlBoundries", as that was the name of the errorId until v1.59. if (errorId == "stlBoundries") { return _suppressions["stlBoundaries"].addFile(file, line); } 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; if (_suppressions.find(errorId) == _suppressions.end()) return false; return _suppressions[errorId].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; if (_suppressions.find(errorId) == _suppressions.end()) return false; return _suppressions[errorId].isSuppressedLocal(file, line); } std::list Suppressions::getUnmatchedLocalSuppressions(const std::string &file) const { std::list r; for (std::map::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i) { if (i->first == "unusedFunction") continue; // unusedFunction is not a "local" suppression std::map >::const_iterator f = i->second._files.find(file); if (f != i->second._files.end()) { for (std::map::const_iterator l = f->second.begin(); l != f->second.end(); ++l) { if (!l->second) { r.push_back(SuppressionEntry(i->first, f->first, l->first)); } } } } return r; } std::list Suppressions::getUnmatchedGlobalSuppressions() const { std::list r; for (std::map::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i) { // 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) { r.push_back(SuppressionEntry(i->first, g->first, l->first)); } } } // unusedFunction.. if (i->first == "unusedFunction") { for (std::map >::const_iterator f = i->second._files.begin(); f != i->second._files.end(); ++f) { for (std::map::const_iterator l = f->second.begin(); l != f->second.end(); ++l) { if (!l->second) { r.push_back(SuppressionEntry(i->first, f->first, l->first)); } } } } } return r; } cppcheck-1.66/lib/suppressions.h000066400000000000000000000130561236713773000167400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include #include "config.h" /// @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; /** * @brief Returns list of unmatched global (glob pattern) suppressions. * @return list of unmatched suppressions */ std::list getUnmatchedGlobalSuppressions() const; }; /// @} //--------------------------------------------------------------------------- #endif // suppressionsH cppcheck-1.66/lib/symboldatabase.cpp000066400000000000000000003602551236713773000175160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "token.h" #include "settings.h" #include "errorlogger.h" #include #include #include #include //--------------------------------------------------------------------------- SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : _tokenizer(tokenizer), _settings(settings), _errorLogger(errorLogger) { // 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; std::map back; // 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 (Token::Match(tok, "class|struct|union|namespace ::| %var% {|:|::") && tok->strAt(-1) != "friend") { const Token *tok2 = tok->tokAt(2); if (tok->strAt(1) == "::") tok2 = tok2->next(); while (tok2 && tok2->str() == "::") tok2 = tok2->tokAt(2); // make sure we have valid code if (!tok2 || !Token::Match(tok2, "{|:")) { // check for qualified variable if (tok2 && tok2->next()) { if (tok2->next()->str() == ";") tok = tok2->next(); else if (Token::simpleMatch(tok2->next(), "= {") && tok2->linkAt(2)->next()->str() == ";") tok = tok2->linkAt(2)->next(); else if (Token::Match(tok2->next(), "(|{") && tok2->next()->link()->strAt(1) == ";") tok = tok2->next()->link()->next(); 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 '{' tok2 = new_scope->definedType->initBaseInfo(tok, tok2); // make sure we have valid code if (!tok2) { break; } } // definition may be different than declaration if (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; } back[tok2->link()] = scope; new_scope->classDef = tok; new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); 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") access[new_scope] = Public; // fill typeList... if (new_scope->isClassOrStruct() || new_scope->type == Scope::eUnion) { 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->definedTypes.push_back(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); } } new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); // make sure we have valid code if (!new_scope->classEnd) { _tokenizer->syntaxError(tok); } // 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 (Token::Match(tok, "namespace %var% %type% (") && _tokenizer->isCPP() && 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 %var% ;") && tok->strAt(-1) != "friend") { if (!findType(tok->next(), scope)) { // fill typeList.. typeList.push_back(Type(tok, 0, scope)); scope->definedTypes.push_back(&typeList.back()); } tok = tok->tokAt(2); } // using namespace else if (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 (tok && Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); } // unnamed struct and union else if (Token::Match(tok, "struct|union {") && Token::Match(tok->next()->link(), "} *|&| %var% ;|[")) { 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)); new_scope->definedType = &typeList.back(); scope->definedTypes.push_back(&typeList.back()); scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope); 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 and union else if (Token::Match(tok, "struct|union {") && Token::simpleMatch(tok->next()->link(), "} ;")) { 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)); new_scope->definedType = &typeList.back(); scope->definedTypes.push_back(&typeList.back()); // 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; } else { // check for end of scope if (tok == scope->classEnd) { if (back.find(tok) != back.end()) { scope = back[tok]; back.erase(tok); } else scope = const_cast(scope->nestedIn); continue; } // check if in class or structure else if (scope->type == Scope::eClass || scope->type == Scope::eStruct) { const Token *funcStart = nullptr; const Token *argStart = 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 %var% :")) { 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)) { if (tok->previous()->str() != "::") { 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->str().find("operator") == 0) { 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; // copy/move constructor? else if (Token::Match(function.tokenDef, "%var% ( const| %var% &|&& &| %var%| )") || Token::Match(function.tokenDef, "%var% ( const| %var% <")) { const Token* typTok = function.tokenDef->tokAt(2); if (typTok->str() == "const") typTok = typTok->next(); if (typTok->strAt(1) == "<") { // TODO: Remove this branch (#4710) if (Token::Match(typTok->linkAt(1), "> & %var%| )")) function.type = Function::eCopyConstructor; else if (Token::Match(typTok->linkAt(1), "> &&|& & %var%| )")) function.type = Function::eMoveConstructor; else function.type = Function::eConstructor; } else if (typTok->strAt(1) == "&&" || typTok->strAt(2) == "&") function.type = Function::eMoveConstructor; else function.type = Function::eCopyConstructor; if (typTok->str() != function.tokenDef->str()) function.type = Function::eConstructor; // Overwrite, if types are not identical } // regular constructor 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 if (tok1->previous()->str() == "virtual") { function.isVirtual = true; break; } // static function else if (tok1->previous()->str() == "static") { function.isStatic = true; break; } // friend function else if (tok1->previous()->str() == "friend") { function.isFriend = true; break; } tok1 = tok1->previous(); } // find the return type if (!function.isConstructor() && !function.isDestructor()) { while (tok1 && Token::Match(tok1->next(), "virtual|static|friend|const|struct|union")) 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++; if (function.type == Function::eCopyConstructor || function.type == Function::eMoveConstructor) scope->numCopyOrMoveConstructors++; // assume implementation is inline (definition and implementation same) function.token = function.tokenDef; function.arg = function.argDef; // out of line function if (Token::simpleMatch(end, ") ;")) { // find the function implementation later tok = end->next(); scope->functionList.push_back(function); } // default or delete else if (Token::Match(end, ") = default|delete ;")) { if (end->strAt(2) == "default") function.isDefault = true; else function.isDelete = true; tok = end->tokAt(3); scope->functionList.push_back(function); } // noexcept; // noexcept = 0; // const noexcept; // const noexcept = 0; else if (Token::Match(end, ") const| noexcept ;|=")) { function.isNoExcept = true; if (end->next()->str() == "const") tok = end->tokAt(3); else tok = end->tokAt(2); if (Token::Match(tok, "= %any% ;")) { function.isPure = true; tok = tok->tokAt(2); } scope->functionList.push_back(function); } // noexcept(...); // noexcept(...) = 0; // const noexcept(...); // const noexcept(...) = 0; else if (Token::Match(end, ") const| noexcept (") && (end->next()->str() == "const" ? Token::Match(end->linkAt(3), ") ;|=") : Token::Match(end->linkAt(2), ") ;|="))) { function.isNoExcept = true; if (end->next()->str() == "const") tok = end->tokAt(3); else tok = end->tokAt(2); if (Token::Match(tok, "= %any% ;")) { function.isPure = true; tok = tok->tokAt(2); } scope->functionList.push_back(function); } // throw(); // throw() = 0; // const throw(); // const throw() = 0; else if (Token::Match(end, ") const| throw (") && (end->next()->str() == "const" ? Token::Match(end->linkAt(3), ") ;|=") : Token::Match(end->linkAt(2), ") ;|="))) { function.isThrow = true; if (end->next()->str() == "const") { if (end->strAt(4) != ")") function.throwArg = end->tokAt(4); tok = end->linkAt(3)->next(); } else { if (end->strAt(3) != ")") function.throwArg = end->tokAt(3); tok = end->linkAt(2)->next(); } if (Token::Match(tok, "= %any% ;")) { function.isPure = true; tok = tok->tokAt(2); } scope->functionList.push_back(function); } // pure virtual function else if (Token::Match(end, ") const| = %any% ;")) { function.isPure = true; if (end->next()->str() == "const") tok = end->tokAt(4); else tok = end->tokAt(3); scope->functionList.push_back(function); } // 'const' or unknown macro (#5197) else if (Token::Match(end, ") %any% ;")) { tok = end->tokAt(2); scope->functionList.push_back(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; } // find start of function '{' while (end && end->str() != "{" && end->str() != ";") end = end->next(); if (!end || end->str() == ";") continue; scope->functionList.push_back(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 (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 = 0; scope->definedType->friendList.push_back(friendInfo); } } else if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) { const Token *funcStart = nullptr; const Token *argStart = nullptr; // function? if (isFunction(tok, scope, &funcStart, &argStart)) { const Token* scopeBegin = argStart->link()->next(); if (scopeBegin->isName()) { // Jump behind 'const' or unknown Macro scopeBegin = scopeBegin->next(); if (scopeBegin->str() == "throw") scopeBegin = scopeBegin->next(); if (scopeBegin->link() && scopeBegin->str() == "(") // Jump behind unknown macro of type THROW(...) scopeBegin = scopeBegin->link()->next(); } // has body? if (scopeBegin->str() == "{" || scopeBegin->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; } } // syntax error? if (!scope) _tokenizer->syntaxError(tok); } // function prototype? else if (scopeBegin->str() == ";") { bool newFunc = true; // Is this function already in the database? for (std::list::const_iterator i = scope->functionList.begin(); i != scope->functionList.end(); ++i) { if (i->tokenDef->str() == tok->str() && Function::argsMatch(scope, i->argDef->next(), argStart->next(), "", 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; } } tok = scopeBegin; 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)); 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 *tok1 = tok->next()->link()->next(); if (tok->str() == "if") scopeList.push_back(Scope(this, tok, scope, Scope::eIf, tok1)); else if (tok->str() == "for") { scopeList.push_back(Scope(this, tok, scope, Scope::eFor, tok1)); } else if (tok->str() == "while") scopeList.push_back(Scope(this, tok, scope, Scope::eWhile, tok1)); else if (tok->str() == "catch") { scopeList.push_back(Scope(this, tok, scope, Scope::eCatch, tok1)); } else if (tok->str() == "switch") scopeList.push_back(Scope(this, tok, scope, Scope::eSwitch, tok1)); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); if (scope->type == Scope::eFor) scope->checkVariable(tok->tokAt(2), Local); // check for variable declaration and add it to new scope if found else if (scope->type == Scope::eCatch) scope->checkVariable(tok->tokAt(2), Throw); // check for variable declaration and add it to new scope if found tok = tok1; } else if (tok->str() == "{") { if (!Token::Match(tok->previous(), "=|,")) { scopeList.push_back(Scope(this, tok, scope, Scope::eUnconditional, tok)); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else { tok = tok->link(); } } } } } // 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) it->derivedFrom[i].type = findType(it->derivedFrom[i].nameTok, it->enclosingScope); } // 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); } } // 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 = findScope(i->start->tokAt(2), &(*it)); if (scope) { // set found scope i->scope = scope; break; } } } } // fill in variable info for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { // find variables it->getVariableList(); } // 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, scope); } } // fill in function scopes for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->type == Scope::eFunction) functionScopes.push_back(&*it); } // fill in class and struct scopes for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->isClassOrStruct()) classAndStructScopes.push_back(&*it); } // 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")) type = type->next(); if (type) func->retType = findTypeInNested(type, func->nestedIn); } } } // 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 = &(*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(); ++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 needInitialization = true; } if (!unknown) { if (needInitialization) scope->definedType->needInitialization = Type::True; else scope->definedType->needInitialization = Type::False; } 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) { scope = &(*it); if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::Unknown) debugMessage(scope->classDef, "SymbolDatabase::SymbolDatabase couldn't resolve all user defined types."); } } // 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 = &(*it); // add all variables std::list::iterator var; for (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 std::list::iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { std::list::iterator arg; for (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 != func->classEnd; tok = tok->next()) { // check for member variable if (tok && 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; } } } } } } /* set all unknown array dimensions that are set by a variable to the maximum size of that variable type */ 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]); // check for a single token dimension that is a variable if (dimension.num == 0) { dimension.known = false; if (!dimension.start || (dimension.start != dimension.end) || !dimension.start->varId()) continue; // 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 } } } } } } } bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart) { // function returning function pointer? '... ( ... %var% ( ... ))( ... ) {' if (tok->str() == "(" && tok->link()->previous()->str() == ")") { const Token* tok2 = tok->link()->next(); if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) { *funcStart = tok->link()->previous()->link()->previous(); *argStart = tok->link()->previous()->link(); return true; } } // regular function? else if (Token::Match(tok, "%var% (") && tok->previous() && (tok->previous()->isName() || tok->strAt(-1) == ">" || tok->strAt(-1) == "&" || tok->strAt(-1) == "*" || // Either a return type in front of tok tok->strAt(-1) == "::" || tok->strAt(-1) == "~" || // or a scope qualifier in front of tok outerScope->isClassOrStruct())) { // or a ctor/dtor const Token* tok2 = tok->next()->link()->next(); if (tok2 && (Token::Match(tok2, "const| ;|{|=") || (Token::Match(tok2, "%var% ;|{") && tok2->isUpperCaseName()) || (Token::Match(tok2, "%var% (") && tok2->isUpperCaseName() && tok2->next()->link()->strAt(1) == "{") || Token::Match(tok2, ": ::| %var% (|::|<|{") || Token::Match(tok2, "= delete|default ;") || Token::Match(tok2, "const| noexcept {|:|;|=") || (Token::Match(tok2, "const| noexcept|throw (") && tok2->str() == "const" ? (tok2->tokAt(2) && Token::Match(tok2->linkAt(2), ") const| {|:|;|=")) : (tok2->next() && Token::Match(tok2->next()->link(), ") {|:|;|="))))) { *funcStart = tok; *argStart = tok->next(); return true; } } // UNKNOWN_MACRO(a,b) { ... } else if (outerScope->type == Scope::eGlobal && Token::Match(tok, "%var% (") && tok->isUpperCaseName() && Token::simpleMatch(tok->linkAt(1), ") {") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { *funcStart = tok; *argStart = tok->next(); return true; } // template constructor? else if (Token::Match(tok, "%var% <") && Token::simpleMatch(tok->next()->link(), "> (")) { const Token* tok2 = tok->next()->link()->next()->link(); if (Token::Match(tok2, ") const| ;|{|=") || Token::Match(tok2, ") : ::| %var% (|::|<|{") || Token::Match(tok->next()->link()->next()->link(), ") const| noexcept {|;|(")) { *funcStart = tok; *argStart = tok2->link(); return true; } } return false; } void Variable::evaluate() { const Token* tok = _start; while (tok && tok->previous() && tok->previous()->isName()) tok = tok->previous(); for (const Token* const end = _name?_name:_end; tok != end;) { if (tok->str() == "static") setFlag(fIsStatic, true); else if (tok->str() == "extern") setFlag(fIsExtern, true); else if (tok->str() == "mutable") setFlag(fIsMutable, true); else if (tok->str() == "const") setFlag(fIsConst, true); else if (tok->str() == "*") { setFlag(fIsPointer, true); 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 (_start && _start->next() && (_start->str() == "static" || _start->str() == "const")) _start = _start->next(); while (_end && _end->previous() && _end->str() == "const") _end = _end->previous(); if (_name) setFlag(fIsArray, arrayDimensions(_dimensions, _name->next())); if (_start) { setFlag(fIsClass, !_start->isStandardType() && !isPointer() && !isReference()); setFlag(fIsStlType, Token::simpleMatch(_start, "std ::")); } 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(_dimensions, tok->next())); } 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; gets simplified to: type var ; var = x ; if (Token::Match(_name, "%var% ; %var% = %any% ;") && _name->strAt(2) == _name->str()) setFlag(fHasDefault, 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(); // skip "struct" if it is C++ if (isCPP) { if (first->str() == "struct") first = first->next(); if (second->str() == "struct") second = second->next(); } // skip const on type passed by value if (Token::Match(first, "const %type% %var%|,|)")) first = first->next(); if (Token::Match(second, "const %type% %var%|,|)")) 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->next() && second->next()->str() == ")"; } } else 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; } } // definition missing variable name else if (first->next()->str() == "," && second->next()->str() != ",") { second = second->next(); // skip default value assignment if (second->next()->str() == "=") { while (!Token::Match(second->next(), ",|)")) second = second->next(); } } else if (first->next()->str() == ")" && second->next()->str() != ")") { second = second->next(); // skip default value assignment if (second->next()->str() == "=") { while (!Token::Match(second->next(), ",|)")) second = 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() != ",") { first = first->next(); // skip default value assignment if (first->next()->str() == "=") { while (!Token::Match(first->next(), ",|)")) first = first->next(); } } else if (second->next()->str() == ")" && first->next()->str() != ")") { first = first->next(); // skip default value assignment if (first->next()->str() == "=") { while (!Token::Match(first->next(), ",|)")) first = 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; // variable names are different else if ((Token::Match(first->next(), "%var% ,|)|=") && Token::Match(second->next(), "%var% ,|)")) && (first->next()->str() != second->next()->str())) { // skip variable names first = first->next(); second = second->next(); } // variable with class path else if (depth && Token::Match(first->next(), "%var%")) { std::string param = path + first->next()->str(); if (Token::Match(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 while (!short_path.empty() && short_path[short_path.size() - 1] != ' ') short_path.resize(short_path.size() - 1); param = short_path + first->next()->str(); if (Token::Match(second->next(), param.c_str())) { second = second->tokAt((int(depth) - 1) * 2); } } } // nested class variable else if (depth == 0 && Token::Match(first->next(), "%var%") && second->next()->str() == scope->className && second->strAt(2) == "::" && first->next()->str() == second->strAt(3)) { second = second->tokAt(2); } first = first->next(); second = second->next(); // skip "struct" if it is C++ if (isCPP) { if (first->str() == "struct") first = first->next(); if (second->str() == "struct") second = second->next(); } // skip const on type passed by value if (Token::Match(first, "const %type% %var%|,|)")) first = first->next(); if (Token::Match(second, "const %type% %var%|,|)")) second = second->next(); } return false; } Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) { Function* function = 0; for (std::list::iterator i = scope->functionList.begin(); i != scope->functionList.end(); ++i) { if (i->tokenDef->str() == tok->str() && Function::argsMatch(scope, i->argDef->next(), argStart->next(), "", 0)) { function = &*i; // copy attributes from function prototype to function Token* to = const_cast(tok); const Token* from = i->tokenDef; to->isAttributeConstructor(from->isAttributeConstructor()); to->isAttributeDestructor(from->isAttributeDestructor()); to->isAttributePure(from->isAttributePure()); to->isAttributeConst(from->isAttributeConst()); to->isAttributeNothrow(from->isAttributeNothrow()); to->isDeclspecNothrow(from->isDeclspecNothrow()); 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 0; } 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 (tok1 && Token::Match(tok1->next(), "static|const")) tok1 = tok1->next(); if (tok1) function.retDef = tok1; scope->functionList.push_back(function); return &scope->functionList.back(); } void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const Token *argStart) { const bool destructor((*tok)->previous()->str() == "~"); const Token *tok1; // skip class/struct name if (destructor) tok1 = (*tok)->tokAt(-3); 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()) { 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) { std::list::iterator func; for (func = scope1->functionList.begin(); func != scope1->functionList.end(); ++func) { if (!func->hasBody && func->tokenDef->str() == (*tok)->str()) { 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()) { if ((func->isConst && (*tok)->next()->link()->next()->str() == "const") || (!func->isConst && (*tok)->next()->link()->next()->str() != "const")) { 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 *new_scope = &scopeList.back(); // skip to start of function bool foundInitLit = false; while (tok1 && (tok1->str() != "{" || (foundInitLit && tok1->previous()->isName()))) { if (tok1->str() == "(" || tok1->str() == "{") tok1 = tok1->link(); if (tok1->str() == ":") foundInitLit = true; tok1 = tok1->next(); } if (tok1) { new_scope->classStart = tok1; new_scope->classEnd = tok1->link(); // syntax error? if (!new_scope->classEnd) { scopeList.pop_back(); while (tok1->next()) tok1 = tok1->next(); *scope = nullptr; *tok = tok1; return; } (*scope)->nestedList.push_back(new_scope); *scope = new_scope; *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, ":|,")) { Type::BaseInfo base; base.isVirtual = false; tok2 = tok2->next(); // check for invalid code if (!tok2 || !tok2->next()) return nullptr; 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->str() == "virtual") { base.isVirtual = true; tok2 = tok2->next(); } base.nameTok = tok2; // handle global namespace if (tok2->str() == "::") { tok2 = tok2->next(); } // handle derived base classes while (Token::Match(tok2, "%var% ::")) { tok2 = tok2->tokAt(2); } base.name = tok2->str(); base.type = nullptr; // add unhandled templates if (tok2->next() && tok2->next()->str() == "<") { tok2 = tok2->next(); base.name += tok2->str(); int level1 = 1; while (tok2->next()) { base.name += tok2->next()->str(); if (tok2->next()->str() == ">") { level1--; if (level1 == 0) break; } else if (tok2->next()->str() == "<") level1++; tok2 = tok2->next(); } } // save pattern for base class name derivedFrom.push_back(base); } if (tok2) // see #4806 tok2 = tok2->next(); } return tok2; } 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) { for (std::list::const_iterator i = classScope->functionList.begin(); i != classScope->functionList.end(); ++i) if (i->name() == funcName) return &*i; } for (std::size_t i = 0; i < derivedFrom.size(); i++) { if (derivedFrom[i].type) { const Function* func = derivedFrom[i].type->getFunction(funcName); if (func) return func; } } return 0; } bool Type::hasCircularDependencies(std::set* anchestors) const { std::set knownAnchestors; if (!anchestors) { anchestors=&knownAnchestors; } 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 (anchestors->find(*parent)!=anchestors->end()) return true; else { anchestors->insert(*parent); if (parent->type->hasCircularDependencies(anchestors)) return true; } } return false; } bool Variable::arrayDimensions(std::vector &dimensions, const Token *tok) { bool isArray = false; const Token *dim = tok; 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()); } dimensions.push_back(dimension); dim = dim->link()->next(); isArray = true; } return isArray; } 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" : "Unknown"); return s; } void SymbolDatabase::printVariable(const Variable *var, const char *indent) const { std::cout << indent << "_name: " << var->nameToken(); if (var->nameToken()) { std::cout << " " << var->name() << " " << _tokenizer->list.fileLine(var->nameToken()) << std::endl; std::cout << indent << " declarationId: " << var->declarationId() << std::endl; } else std::cout << std::endl; std::cout << indent << "_start: " << var->typeStartToken() << " " << var->typeStartToken()->str() << " " << _tokenizer->list.fileLine(var->typeStartToken()) << std::endl; std::cout << indent << "_end: " << var->typeEndToken() << " " << var->typeEndToken()->str() << " " << _tokenizer->list.fileLine(var->typeEndToken()) << 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" : "???") << std::endl; std::cout << indent << "_flags: " << std::endl; std::cout << indent << " isMutable: " << (var->isMutable() ? "true" : "false") << std::endl; std::cout << indent << " isStatic: " << (var->isStatic() ? "true" : "false") << std::endl; std::cout << indent << " isExtern: " << (var->isExtern() ? "true" : "false") << std::endl; std::cout << indent << " isLocal: " << (var->isLocal() ? "true" : "false") << std::endl; std::cout << indent << " isConst: " << (var->isConst() ? "true" : "false") << std::endl; std::cout << indent << " isClass: " << (var->isClass() ? "true" : "false") << std::endl; std::cout << indent << " isArray: " << (var->isArray() ? "true" : "false") << std::endl; std::cout << indent << " isPointer: " << (var->isPointer() ? "true" : "false") << std::endl; std::cout << indent << " isReference: " << (var->isReference() ? "true" : "false") << std::endl; std::cout << indent << " isRValueRef: " << (var->isRValueReference() ? "true" : "false") << std::endl; std::cout << indent << " hasDefault: " << (var->hasDefault() ? "true" : "false") << std::endl; std::cout << indent << " isStlType: " << (var->isStlType() ? "true" : "false") << std::endl; std::cout << indent << "_type: "; if (var->type()) { std::cout << var->type()->name(); if (var->typeScope()) std::cout << " " << var->typeScope()->type; std::cout << " " << _tokenizer->list.fileLine(var->type()->classDef) << std::endl; } else std::cout << "none" << std::endl; std::cout << indent << "_scope: "; if (var->scope()) { std::cout << var->scope()->className << " " << var->scope()->type; if (var->scope()->classDef) std::cout << " " << _tokenizer->list.fileLine(var->scope()->classDef) << std::endl; else std::cout << std::endl; } else std::cout << "none" << 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 { if (title) std::cout << "\n### " << title << " ###\n"; for (std::list::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { std::cout << "Scope: " << &*scope << std::endl; std::cout << " type: " << scope->type << std::endl; std::cout << " className: " << scope->className << std::endl; std::cout << " classDef: " << scope->classDef; if (scope->classDef) std::cout << " " << scope->classDef->str() << " " << _tokenizer->list.fileLine(scope->classDef) << std::endl; else std::cout << std::endl; std::cout << " classStart: " << scope->classStart; if (scope->classStart) std::cout << " " << scope->classStart->str() << " " << _tokenizer->list.fileLine(scope->classStart) << std::endl; else std::cout << std::endl; std::cout << " classEnd: " << scope->classEnd; if (scope->classEnd) std::cout << " " << scope->classEnd->str() << " " << _tokenizer->list.fileLine(scope->classEnd) << std::endl; else std::cout << 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: " << func->tokenDef->str() << " " << _tokenizer->list.fileLine(func->tokenDef) << 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" : "???") << std::endl; std::cout << " access: " << (func->access == Public ? "Public" : func->access == Protected ? "Protected" : func->access == Private ? "Private" : "???") << std::endl; std::cout << " hasBody: " << (func->hasBody ? "true" : "false") << std::endl; std::cout << " isInline: " << (func->isInline ? "true" : "false") << std::endl; std::cout << " isConst: " << (func->isConst ? "true" : "false") << std::endl; std::cout << " isVirtual: " << (func->isVirtual ? "true" : "false") << std::endl; std::cout << " isPure: " << (func->isPure ? "true" : "false") << std::endl; std::cout << " isStatic: " << (func->isStatic ? "true" : "false") << std::endl; std::cout << " isFriend: " << (func->isFriend ? "true" : "false") << std::endl; std::cout << " isExplicit: " << (func->isExplicit ? "true" : "false") << std::endl; std::cout << " isDefault: " << (func->isDefault ? "true" : "false") << std::endl; std::cout << " isDelete: " << (func->isDelete ? "true" : "false") << std::endl; std::cout << " isNoExcept: " << (func->isNoExcept ? "true" : "false") << std::endl; std::cout << " isThrow: " << (func->isThrow ? "true" : "false") << std::endl; std::cout << " isOperator: " << (func->isOperator ? "true" : "false") << std::endl; std::cerr << " isAttributeConst: " << (func->isAttributeConst() ? "true" : "false") << std::endl; std::cerr << " isAttributePure: " << (func->isAttributePure() ? "true" : "false") << std::endl; std::cerr << " isAttributeNothrow: " << (func->isAttributeNothrow() ? "true" : "false") << std::endl; std::cerr << " isDeclspecNothrow: " << (func->isDeclspecNothrow() ? "true" : "false") << 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: " << func->tokenDef->str() << " " <<_tokenizer->list.fileLine(func->tokenDef) << std::endl; std::cout << " argDef: " << _tokenizer->list.fileLine(func->argDef) << std::endl; if (!func->isConstructor() && !func->isDestructor()) std::cout << " retDef: " << func->retDef->str() << " " <<_tokenizer->list.fileLine(func->retDef) << std::endl; std::cout << " retType: " << func->retType << std::endl; if (func->hasBody) { std::cout << " token: " << _tokenizer->list.fileLine(func->token) << std::endl; std::cout << " arg: " << _tokenizer->list.fileLine(func->arg) << std::endl; } std::cout << " nestedIn: "; if (func->nestedIn) { std::cout << func->nestedIn->className << " " << func->nestedIn->type; if (func->nestedIn->classDef) std::cout << " " << _tokenizer->list.fileLine(func->nestedIn->classDef) << std::endl; else std::cout << std::endl; } else std::cout << "Unknown" << std::endl; std::cout << " functionScope: "; if (func->functionScope) { std::cout << func->functionScope->className << " " << _tokenizer->list.fileLine(func->functionScope->classDef) << std::endl; } else std::cout << "Unknown" << 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, " "); } 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: " << scope->functionOf; if (scope->functionOf) { std::cout << " " << scope->functionOf->type << " " << scope->functionOf->className; if (scope->functionOf->classDef) std::cout << " " << _tokenizer->list.fileLine(scope->functionOf->classDef); } std::cout << std::endl; std::cout << " function: " << scope->function; if (scope->function) { std::cout << " " << scope->function->tokenDef->str() << " " << _tokenizer->list.fileLine(scope->function->tokenDef); } 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: " << _tokenizer->list.fileLine(type->classDef) << 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; } } static std::string toxml(const std::string &str) { std::ostringstream xml; 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; default: xml << c; break; } } return xml.str(); } void SymbolDatabase::printXml(std::ostream &out) const { // Scopes.. out << " " << std::endl; for (std::list::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { out << " type << "\""; if (!scope->className.empty()) out << " className=\"" << 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 << " 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() ? "true" : "false") << '\"'; out << " isArray=\"" << (var->isArray() ? "true" : "false") << '\"'; out << " isClass=\"" << (var->isClass() ? "true" : "false") << '\"'; out << " isLocal=\"" << (var->isLocal() ? "true" : "false") << '\"'; out << " isPointer=\"" << (var->isPointer() ? "true" : "false") << '\"'; out << " isReference=\"" << (var->isReference() ? "true" : "false") << '\"'; out << " isStatic=\"" << (var->isStatic() ? "true" : "false") << '\"'; out << "/>" << std::endl; } out << " " << std::endl; } //--------------------------------------------------------------------------- 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 )")) { unsigned int count = 0; for (const Token* tok = start->next(); tok; tok = tok->next()) { const Token* startTok = tok; const Token* endTok = nullptr; const Token* nameTok = nullptr; if (tok->str() == "," || tok->str() == ")") return; // Syntax error 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->tokAt(startTok->str() == "const" ? 1 : 0); if (typeTok->str() == "struct") typeTok = typeTok->next(); // 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 = startTok; } else endTok = tok->previous(); } const ::Type *argType = nullptr; if (!typeTok->isStandardType()) { argType = symbolDatabase->findVariableType(scope, typeTok); if (!argType) { // look for variable type in any using namespace in this scope or above const Scope *parent = scope; while (parent) { 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) break; } } parent = parent->nestedIn; } } } // skip default values if (tok->str() == "=") { while (tok->str() != "," && tok->str() != ")") { if (tok->link() && Token::Match(tok, "[{[(<]")) tok = tok->link(); tok = tok->next(); } } argumentList.push_back(Variable(nameTok, startTok, endTok, count++, Argument, argType, functionScope)); if (tok->str() == ")") 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; std::list::const_iterator func; // check if function defined in base class for (func = parent->functionList.begin(); func != parent->functionList.end(); ++func) { if (func->isVirtual && func->tokenDef->str() == tokenDef->str()) { // 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()) { returnMatch = false; break; } temp1 = temp1->previous(); temp2 = temp2->previous(); } // check for matching function parameters if (returnMatch && argsMatch(baseType->classScope, func->argDef, argDef, "", 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 0; } return 0; } //--------------------------------------------------------------------------- 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) { } 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) { const Token *nameTok = classDef; if (!classDef) { type = Scope::eGlobal; } else if (classDef->str() == "class") { 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 { type = Scope::eFunction; } // skip over qualification if present if (nameTok && nameTok->str() == "::") nameTok = nameTok->next(); while (nameTok && Token::Match(nameTok, "%type% ::")) nameTok = nameTok->tokAt(2); if (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 Token *start; if (classStart) start = classStart->next(); // global scope else if (className.empty()) start = check->_tokenizer->tokens(); // forward declaration else return; AccessControl varaccess = defaultAccess(); unsigned int level = 1; for (const Token *tok = start; tok; tok = tok->next()) { // end of scope? if (tok->str() == "}") { level--; if (level == 0) break; } // syntax error? else if (tok->next() == nullptr) break; // Is it a function? else if (tok->str() == "{") { tok = tok->link(); // syntax error? if (!tok) return; 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(), "} %var% ;|[")) { tok = tok->next()->link()->tokAt(2); continue; } else if (Token::simpleMatch(tok->next()->link(), "} ;")) { level++; 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 %var% ;")) { tok = tok->tokAt(2); continue; } // Borland C++: Ignore properties.. else if (tok->str() == "__property") continue; // skip return and delete else if (Token::Match(tok, "return|delete")) { 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 (Token::Match(tok, ";|{|}")) continue; else if (Token::Match(tok, "goto %var% ;")) { tok = tok->tokAt(2); continue; } tok = checkVariable(tok, varaccess); if (!tok) break; } } const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess) { // This is the start of a statement const Token *vartok = nullptr; const Token *typetok = nullptr; // 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, "struct|union")) { tok = tok->next(); } 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 (tok && tok->str() == "[") 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 = check->findVariableType(this, typetok); if (!vType) { // look for variable type in any using namespace in this scope or above const Scope *parent = this; while (parent) { for (std::list::const_iterator ui = parent->usingList.begin(); ui != parent->usingList.end(); ++ui) { if (ui->scope) { vType = check->findVariableType(ui->scope, typetok); if (vType) break; } } parent = parent->nestedIn; } } } addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this); } 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, "*|&|&&") || (tok && tok->str() == "(" && 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* tok, const Token*& vartok, const Token*& typetok) const { if (tok && (tok->str() == "throw" || tok->str() == "new") && check->_tokenizer->isCPP()) return false; const Token* localTypeTok = skipScopeIdentifiers(tok); const Token* localVarTok = nullptr; if (Token::Match(localTypeTok, "%type% <")) { const Token* closeTok = localTypeTok->next()->link(); if (closeTok) { localVarTok = skipPointers(closeTok->next()); if (Token::Match(localVarTok, ":: %type% %var% ;|=|(")) { if (localVarTok->strAt(3) != "(" || Token::simpleMatch(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 && localVarTok->str() == "const") localVarTok = localVarTok->next(); if (Token::Match(localVarTok, "%var% ;|=")) { vartok = localVarTok; typetok = localTypeTok; } else if (Token::Match(localVarTok, "%var% )|[") && localVarTok->str() != "operator") { vartok = localVarTok; typetok = localTypeTok; } else if ((isLocal() || type == Scope::eFunction) && Token::Match(localVarTok, "%var% (") && Token::simpleMatch(localVarTok->next()->link(), ") ;") && localVarTok->varId()) { vartok = localVarTok; typetok = localTypeTok; } else if (type == eCatch && Token::Match(localVarTok, "%var% )")) { vartok = localVarTok; typetok = localTypeTok; } return nullptr != vartok; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const { std::list::const_iterator type; for (type = typeList.begin(); type != typeList.end(); ++type) { // do the names match? if (type->name() == typeTok->str()) { // check if type does not have a namespace if (typeTok->strAt(-1) != "::") { const Scope *parent = start; // check if in same namespace while (parent) { // out of line class function belongs to class if (parent->type == Scope::eFunction && parent->functionOf) parent = parent->functionOf; else if (parent != type->enclosingScope) parent = parent->nestedIn; else break; } if (type->enclosingScope == parent) return &(*type); } // type has a namespace else { // FIXME check if namespace path matches supplied path return &(*type); } } } return nullptr; } //--------------------------------------------------------------------------- /** @todo This function only counts the number of arguments in the function call. It does not take into account function constantness. It does not take into account argument types. This can be difficult because of promotion and conversion operators and casts and because the argument can also be a function call. */ const Function* Scope::findFunction(const Token *tok) const { for (std::list::const_iterator i = functionList.begin(); i != functionList.end(); ++i) { if (i->tokenDef->str() == tok->str()) { const Function *func = &*i; if ((tok->strAt(1) == "(" || (func->name() == tok->str() && tok->strAt(1) == "{" && func->type == Function::eConstructor)) && tok->tokAt(2)) { std::string end(tok->strAt(1) == "{" ? "}" : ")"); // check the arguments unsigned int args = 0; const Token *arg = tok->tokAt(2); while (arg && arg->str() != end) { /** @todo check argument type for match */ // mismatch parameter: passing parameter by address to function, argument is reference if (arg->str() == "&") { // check that function argument type is not mismatching const Variable *funcarg = func->getArgumentVar(args); if (funcarg && funcarg->isReference()) { args = ~0U; break; } } args++; arg = arg->nextArgument(); } // check for argument count match or default arguments if (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount())) return func; } } } // check in base classes if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { for (std::size_t i = 0; i < definedType->derivedFrom.size(); ++i) { const Type *base = definedType->derivedFrom[i].type; if (base && base->classScope) { if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already continue; const Function * func = base->classScope->findFunction(tok); if (func) return func; } } } return 0; } //--------------------------------------------------------------------------- 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% (")) { currScope = currScope->findRecordInNestedList(tok1->strAt(2)); tok1 = tok1->tokAt(2); } tok1 = tok1->tokAt(2); if (currScope && tok1) return currScope->findFunction(tok1); } } // check for member function else if (Token::Match(tok->tokAt(-2), "!!this .")) { if (Token::Match(tok->tokAt(-2), "%var% .")) { const Token *tok1 = tok->tokAt(-2); if (tok1->varId()) { const Variable *var = getVariableFromVarId(tok1->varId()); if (var && var->typeScope()) return var->typeScope()->findFunction(tok); } } } // check in enclosing scopes else { while (currScope) { const Function *func = currScope->findFunction(tok); if (func) return func; currScope = currScope->nestedIn; } } return 0; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- const Type* Scope::findType(const std::string & name) const { std::list::const_iterator it; for (it = definedTypes.begin(); it != definedTypes.end(); ++it) { if ((*it)->name() == name) return (*it); } return 0; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- unsigned int Scope::getNestedNonFunctions() const { unsigned int nested = 0; std::list::const_iterator ni; for (ni = nestedList.begin(); ni != nestedList.end(); ++ni) { if ((*ni)->type != Scope::eFunction) nested++; } return nested; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- 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()) return startScope->definedType; // absolute path - directly start in global scope if (startTok->str() == "::") { startTok = startTok->next(); startScope = &scopeList.front(); } const Token* tok = startTok; const Scope* scope = startScope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::") { scope = scope->findRecordInNestedList(tok->str()); if (scope) { tok = tok->tokAt(2); } else { startScope = startScope->nestedIn; if (!startScope) break; scope = startScope; tok = startTok; } } else return scope->findType(tok->str()); } // not a valid path return 0; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findTypeInNested(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()) 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 0; } //--------------------------------------------------------------------------- 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 0; } //--------------------------------------------------------------------------- Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *ns) { const Function * function = nullptr; std::list::const_iterator it; for (it = ns->functionList.begin(); it != ns->functionList.end(); ++it) { if (it->name() == func->str()) { if (Function::argsMatch(ns, func->tokAt(2), it->argDef->next(), "", 0)) { function = &*it; break; } } } if (!function) { const Scope * scope = ns->findRecordInNestedList(func->str()); if (scope && func->strAt(1) == "::") { function = findFunctionInScope(func->tokAt(2), scope); } } return const_cast(function); } cppcheck-1.66/lib/symboldatabase.h000066400000000000000000000627441236713773000171650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include #include #include "config.h" #include "token.h" #include "mathlib.h" class Tokenizer; class Settings; class ErrorLogger; class Scope; class SymbolDatabase; /** * @brief Access control enumerations. */ enum AccessControl { Public, Protected, Private, Global, Namespace, Argument, Local, Throw }; /** * @brief Array dimension information. */ struct Dimension { Dimension() : start(NULL), end(NULL), 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(NULL), nameTok(NULL), 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(NULL), nameEnd(NULL), type(NULL) { } const Token* nameStart; const Token* nameEnd; std::string name; const Type* type; }; std::vector derivedFrom; std::list friendList; Type(const Token* classDef_ = 0, const Scope* classScope_ = 0, const Scope* enclosingScope_ = 0) : classDef(classDef_), classScope(classScope_), enclosingScope(enclosingScope_), needInitialization(Unknown) { } const std::string& name() const { const Token* next = classDef->next(); if (next->isName()) return next->str(); return emptyString; } 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 anchestors list of anchestors. For internal usage only, clients should not supply this argument. * @return true if there is a circular dependency */ bool hasCircularDependencies(std::set* anchestors = 0) const; }; /** @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::') */ }; /** * Get specified flag state. * @param flag_ flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(int flag_) const { return bool((_flags & flag_) != 0); } /** * Set specified flag state. * @param flag_ flag to set state * @param state_ new state of flag */ void setFlag(int flag_, bool state_) { _flags = state_ ? _flags | flag_ : _flags & ~flag_; } /** * @brief parse and save array dimension information * @param dimensions array dimensions vector * @param tok the first '[' token of array declaration * @return true if array, false if not */ static bool arrayDimensions(std::vector &dimensions, const Token *tok); public: Variable(const Token *name_, const Token *start_, const Token *end_, std::size_t index_, AccessControl access_, const Type *type_, const Scope *scope_) : _name(name_), _start(start_), _end(end_), _index(index_), _access(access_), _flags(0), _type(type_), _scope(scope_) { evaluate(); } /** * 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 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 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); } /** * Is pointer variable. * @return true if pointer, false otherwise */ bool isPointer() const { return getFlag(fIsPointer); } /** * 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 : 0; } /** * 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 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 stlTypes array of stl types in alphabetical order * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ template bool isStlType(const char* const(&stlTypes)[array_length]) const { return isStlType() && std::binary_search(stlTypes, stlTypes + array_length, _start->strAt(2)); } /** * 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) */ bool isFloatingType() const { return (typeStartToken()->str() == "float" || typeStartToken()->str() == "double") && !isArrayOrPointer() ; } /** * Determine whether it's an integral number type * @return true if the type is known and it's an integral type (bool, char, short, int, long long and their unsigned counter parts) */ bool isIntegralType() const { return typeStartToken()->str() == "bool" || typeStartToken()->str() == "char" || typeStartToken()->str() == "short" || typeStartToken()->str() == "int" || typeStartToken()->str() == "long"; } 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 */ 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(); }; class CPPCHECKLIB Function { public: enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction }; Function() : tokenDef(NULL), argDef(NULL), token(NULL), arg(NULL), retDef(NULL), retType(NULL), functionScope(NULL), nestedIn(NULL), initArgCount(0), type(eFunction), access(Public), hasBody(false), isInline(false), isConst(false), isVirtual(false), isPure(false), isStatic(false), isFriend(false), isExplicit(false), isDefault(false), isDelete(false), isNoExcept(false), isThrow(false), isOperator(false), noexceptArg(nullptr), throwArg(nullptr) { } 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 isAttributeNothrow() const { return tokenDef->isAttributeNothrow(); } bool isDeclspecNothrow() const { return tokenDef->isDeclspecNothrow(); } 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 bool hasBody; // has implementation bool isInline; // implementation in class definition bool isConst; // is const bool isVirtual; // is virtual bool isPure; // is pure virtual bool isStatic; // is static bool isFriend; // is friend bool isExplicit; // is explicit bool isDefault; // is default bool isDelete; // is delete bool isNoExcept; // is noexcept bool isThrow; // is throw bool isOperator; // is operator const Token *noexceptArg; const Token *throwArg; static bool argsMatch(const Scope *info, const Token *first, const Token *second, const std::string &path, unsigned int depth); private: bool isImplicitlyVirtual_rec(const ::Type* type, bool& safe) const; }; 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 }; 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::list varlist; const Scope *nestedIn; std::list nestedList; unsigned int numConstructors; unsigned int numCopyOrMoveConstructors; std::list usingList; ScopeType type; Type* definedType; std::list definedTypes; // function specific fields const Scope *functionOf; // scope this function belongs to Function *function; // function info for this function bool isClassOrStruct() const { return (type == eClass || type == eStruct); } bool isExecutable() const { return type != eClass && type != eStruct && type != eUnion && type != eGlobal && type != eNamespace; } bool isLocal() const { return (type == eIf || type == eElse || type == eFor || type == eWhile || type == eDo || type == eSwitch || type == eUnconditional || type == eTry || type == eCatch); } /** * @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; /** * @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(static_cast(this)->findRecordInNestedList(name)); } const Type* findType(const std::string& name) const; Type* findType(const std::string& name) { return const_cast(static_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_) { varlist.push_back(Variable(token_, start_, end_, varlist.size(), access_, type_, scope_)); } /** @brief initialize varlist */ void getVariableList(); const Function *getDestructor() const; /** * @brief get the number of nested scopes that are not functions * * @return the number of user defined types (class, struct, union) * that are defined in this user defined type or namespace. */ unsigned int getNestedNonFunctions() const; 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 * @return pointer to last token */ const Token *checkVariable(const Token *tok, AccessControl varaccess); /** * @brief get variable from name * @param varname name of variable * @return pointer to variable */ const Variable *getVariable(const std::string &varname) const; private: /** * @brief helper function for getVariableList() * @param tok pointer to token to check * @param vartok populated with pointer to the variable token, if found * @param typetok populated with pointer to the type token, if found * @return true if tok points to a variable declaration, false otherwise */ bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok) const; }; class CPPCHECKLIB SymbolDatabase { public: SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); /** @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 type token containing variable type * @return pointer to type if found or NULL if not found */ const Type *findVariableType(const Scope *start, const Token *type) 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 *tok, const Scope *startScope) const; Type* findType(const Token *tok, Scope *startScope) const { return const_cast(this->findType(tok, static_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, static_cast(startScope))); } bool isClassOrStruct(const std::string &type) const { for (std::list::const_iterator i = typeList.begin(); i != typeList.end(); ++i) if (i->name() == type) return true; return false; } const Variable *getVariableFromVarId(std::size_t varId) const { return _variableList[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 = NULL) const; void printVariable(const Variable *var, const char *indent) const; void printXml(std::ostream &out) const; bool isCPP() const; private: friend class Scope; void addClassFunction(Scope **info, 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 **info, const Token **tok); static bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart); const Type *findTypeInNested(const Token *tok, const Scope *startScope) const; const Scope *findNamespace(const Token * tok, const Scope * scope) const; Function *findFunctionInScope(const Token *func, const Scope *ns); const Tokenizer *_tokenizer; const Settings *_settings; ErrorLogger *_errorLogger; /** variable symbol table */ std::vector _variableList; /** list for missing types */ std::list _blankTypes; }; //--------------------------------------------------------------------------- #endif // symboldatabaseH cppcheck-1.66/lib/templatesimplifier.cpp000066400000000000000000001476551236713773000204320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mathlib.h" #include "token.h" #include "tokenize.h" #include "errorlogger.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #ifdef GDB_HELPERS #include static void printlist(const std::list &list) { for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) { const Token *token = *it; std::cout << " "; while (token && !Token::Match(token, "[{};]")) { std::cout << " " << token->str(); token = token->next(); } std::cout << std::endl; } } static void printvector(const std::vector &v) { for (std::size_t i = 0; i < v.size(); i++) { const Token *token = v[i]; std::cout << " " << i << ":"; while (token && !Token::Match(token, "[{};]")) { std::cout << " " << token->str(); token = token->next(); } std::cout << std::endl; } } #endif //--------------------------------------------------------------------------- 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 < > %var%")) { const Token *end = tok; while (end) { if (end->str() == ";") break; if (end->str() == "{") { end = end->link()->next(); break; } if (!Token::Match(end, "%var%|::|<|>|>>|,")) { 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% ,") || Token::Match(tok2, "%num% ,")) { type += tok2->str() + ","; tok2 = tok2->tokAt(2); } if (Token::Match(tok2, "%type% > (") || Token::Match(tok2, "%num% > (")) { type += tok2->str(); tok->str(tok->str() + "<" + type + ">"); Token::eraseTokens(tok, tok2->tokAt(2)); if (tok == tokens) goback = true; } } } } const Token* TemplateSimplifier::hasComplicatedSyntaxErrorsInTemplates(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(); // 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(); } } // not start of statement? if (tok->previous() && !Token::Match(tok, "[;{}]")) continue; // skip starting tokens.. ;;; typedef typename foo::bar::.. while (Token::Match(tok, "[;{}]")) tok = tok->next(); while (Token::Match(tok, "typedef|typename")) tok = tok->next(); while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); if (!tok) break; // template variable or type.. if (Token::Match(tok, "%type% <")) { // these are used types.. std::set usedtypes; // parse this statement and see if the '<' and '>' are matching unsigned int level = 0; for (const Token *tok2 = tok; tok2 && !Token::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); return tok; } } } return 0; } 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, "& ::| %var%")) tok = tok->next(); // Skip 'typename...' (Ticket #5774) if (Token::simpleMatch(tok, "typename . . .")) { tok = tok->tokAt(4); continue; } // Skip '=' if (tok && tok->str() == "=") 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, "%var% ::")) { tok = tok->tokAt(2); if (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->type() != Token::eChar && !tok->isName()) 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 (tok && (tok->str() == "(" || tok->str() == "[")) { tok = tok->link()->next(); while (tok && 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 (tok->str() == ">" || tok->str() == ">>") { if (level == 0) return numberOfParameters; --level; if (tok->str() == ">>") { if (level == 0) return numberOfParameters; --level; } tok = tok->next(); // * / & while (Token::Match(tok, "[*&]")) tok = tok->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(const_cast(tok2->next()->link())))) { 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 %var% [,)]")) { 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::simpleMatch(tok3, "> (")) continue; s = ostr.str(); } // save search pattern.. const std::string pattern(s + " > ("); // remove spaces to create new name s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); const std::string name(s + ">"); expandedtemplates.insert(name); // Rename template.. Token::eraseTokens(tok2, Token::findsimplematch(tok2, "(")); tok2->str(name); // delete the "template < >" tok->deleteNext(2); tok->deleteThis(); // Use this special template in the code.. while (nullptr != (tok2 = const_cast(Token::findmatch(tok2, pattern.c_str())))) { Token::eraseTokens(tok2, Token::findsimplematch(tok2, "(")); tok2->str(name); } } return expandedtemplates; } std::list TemplateSimplifier::getTemplateDeclarations(Token *tokens, bool &codeWithTemplates) { std::list templates; for (Token *tok = tokens; tok; tok = tok->next()) { // TODO: handle namespaces. Right now we don't instantiate templates that are defined in namespaces. if (Token::Match(tok, "namespace %type% {")) tok = tok->linkAt(2); if (Token::simpleMatch(tok, "template <")) { Token *parmEnd = tok->next()->findClosingBracket(); codeWithTemplates = true; for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { // Just a declaration => ignore this if (tok2->str() == ";") break; // Implementation => add to "templates" if (tok2->str() == "{") { templates.push_back(tok); break; } } } } return templates; } std::list TemplateSimplifier::getTemplateInstantiations(Token *tokens) { std::list used; for (Token *tok = tokens; tok; tok = tok->next()) { // template definition.. skip it if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (!tok) break; } else if (Token::Match(tok->previous(), "[({};=] %var% <") || Token::Match(tok->previous(), "%type% %var% <") || Token::Match(tok->tokAt(-2), "[,:] private|protected|public %var% <")) { // 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 for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { if (Token::Match(tok2, ", %var% <") && TemplateSimplifier::templateParameters(tok2->tokAt(2))) { used.push_back(tok2->next()); } } // Add outer template.. if (TemplateSimplifier::templateParameters(tok->next())) used.push_back(tok); } } return used; } 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; tok; tok = tok->next()) { if (Token::simpleMatch(tok, "template < >")) { // Ticket #5762: Skip specialization tokens tok = tok->tokAt(2); continue; } if (tok->str() == "<" && templateParameters(tok)) ++templateParmDepth; // end of template parameters? if (tok->str() == ">" || tok->str() == ">>") { if (Token::Match(tok, ">|>> class|struct %var%")) classname = tok->strAt(2); templateParmDepth -= (1 + (tok->str() == ">>")); if (0 == templateParmDepth) break; } // 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; if (!Token::Match(tok, (classname + " < %any%").c_str())) continue; // count the parameters.. tok = tok->next(); 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()) { tok->insertToken(","); tok = tok->next(); const Token *from = (*it)->next(); std::stack links; while (from && (!links.empty() || (from->str() != "," && from->str() != ">"))) { 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 (tok2->str() == "(") tok2 = tok2->link(); else if (Token::Match(tok2, "%type% <") && templateParameters(tok2->next())) { std::list::iterator ti = std::find(templateInstantiations->begin(), templateInstantiations->end(), tok2); if (ti != templateInstantiations->end()) templateInstantiations->erase(ti); ++indentlevel; } else if (indentlevel > 0 && tok2->str() == ">") --indentlevel; else if (indentlevel > 0 && tok2->str() == ">>") { indentlevel -= 2; if (indentlevel < 0) tok2->str(">"); } else if (indentlevel == 0 && Token::Match(tok2, ",|>|>>")) break; if (indentlevel < 0) break; } Token::eraseTokens(eqtok, tok2); eqtok->deleteThis(); } } } bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::string &name, 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->str() != ">>" || indentlevel > 1); tok = tok->next()) { if (Token::Match(tok, "[<,] %var% <") && templateParameters(tok->tokAt(2)) > 0) ++indentlevel; if (indentlevel > 0 && tok->str() == ">") --indentlevel; if (indentlevel > 0 && tok->str() == ">>") indentlevel -= (indentlevel > 1) ? 2 : 1; } if (!tok || !Token::Match(tok->next(), patternAfter)) return false; } // nothing mismatching was found.. return true; } int TemplateSimplifier::getTemplateNamePosition(const Token *tok) { // get the position of the template name int namepos = 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 { // Name not found return -1; } if ((tok->strAt(namepos) == "*" || tok->strAt(namepos) == "&")) ++namepos; return namepos; } void TemplateSimplifier::expandTemplate( TokenList& tokenlist, const Token *tok, const std::string &name, std::vector &typeParametersInDeclaration, const std::string &newName, std::vector &typesUsedInTemplateInstantiation, std::list &templateInstantiations) { for (const Token *tok3 = tokenlist.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { if (tok3->str() == "{" || tok3->str() == "(" || tok3->str() == "[") tok3 = tok3->link(); // Start of template.. if (tok3 == tok) { tok3 = tok3->next(); } // member function implemented outside class definition else if (TemplateSimplifier::instantiateMatch(tok3, name, typeParametersInDeclaration.size(), ":: ~| %var% (")) { 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; int indentlevel = 0; std::stack brackets; // holds "(", "[" and "{" tokens 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::Match(typetok, "%var% <") && templateParameters(typetok->next()) > 0) ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">>") { if (typeindentlevel == 1) break; typeindentlevel -= 2; } tokenlist.addtoken(typetok, tok3->linenr(), tok3->fileIndex()); } continue; } } // replace name.. if (Token::Match(tok3, (name + " !!<").c_str())) { tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex()); continue; } // copy tokenlist.addtoken(tok3, tok3->linenr(), tok3->fileIndex()); if (Token::Match(tok3, "%type% <")) { //if (!Token::simpleMatch(tok3, (name + " <").c_str())) //done = false; templateInstantiations.push_back(tokenlist.back()); } // link() newly tokens manually else if (tok3->str() == "{") { brackets.push(tokenlist.back()); indentlevel++; } 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 (indentlevel <= 1 && brackets.empty()) { // there is a bug if indentlevel is 0 // the "}" token should only be added if indentlevel is 1 but I add it always intentionally // if indentlevel ever becomes 0, cppcheck will write: // ### Error: Invalid number of character { break; } --indentlevel; } 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 isLowerThanOr(const Token* lower) { return lower->isAssignmentOp() || Token::Match(lower, "}|;|(|[|]|)|,|?|:|%oror%|&&|return|throw|case"); } 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) || Token::Match(lower, "%comp%|&"); } static bool isLowerThanPlusMinus(const Token* lower) { return isLowerThanShift(lower) || lower->str() == "<<" || lower->str() == ">>"; } static bool isLowerThanMulDiv(const Token* lower) { return isLowerThanPlusMinus(lower) || lower->str() == "+" || lower->str() == "-"; } static bool isLowerEqualThanMulDiv(const Token* lower) { return isLowerThanMulDiv(lower) || Token::Match(lower, "[*/%]"); } static std::string ShiftInt(const char cop, const Token* left, const Token* right) { if (cop == '&' || cop == '|' || cop == '^') return MathLib::calculate(left->str(), right->str(), cop); const MathLib::bigint leftInt = MathLib::toLongNumber(left->str()); const MathLib::bigint rightInt = MathLib::toLongNumber(right->str()); const bool rightIntIsPositive = rightInt >= 0; const bool leftIntIsPositive = leftInt >= 0; const bool leftOperationIsNotLeftShift = left->previous()->str() != "<<"; const bool operandIsLeftShift = right->previous()->str() == "<<"; if (cop == '<') { // Ensure that its not a shift operator as used for streams if (leftOperationIsNotLeftShift && operandIsLeftShift && rightIntIsPositive) { if (!leftIntIsPositive) { // In case the left integer is negative, e.g. -1000 << 16. Do not simplify. return left->str() + " << " + right->str(); } return MathLib::toString(leftInt << rightInt); } } else if (rightIntIsPositive) { return MathLib::toString(leftInt >> rightInt); } return ""; } static std::string ShiftUInt(const char cop, const Token* left, const Token* right) { if (cop == '&' || cop == '|' || cop == '^') return MathLib::calculate(left->str(), right->str(), cop); const MathLib::biguint leftInt=MathLib::toULongNumber(left->str()); const MathLib::biguint rightInt=MathLib::toULongNumber(right->str()); if (cop == '<') { if (left->previous()->str() != "<<") // Ensure that its not a shift operator as used for streams return MathLib::toString(leftInt << rightInt); } else { return MathLib::toString(leftInt >> rightInt); } return ""; } 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% /") && (tok->strAt(3) != "0") && tok->next()->str() == MathLib::multiply(tok->strAt(3), MathLib::divide(tok->next()->str(), tok->strAt(3)))) { // 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 break; tok = tok->next(); // Don't simplify "%num% / 0" if (Token::Match(op, "[/%] 0")) continue; // Integer operations if (Token::Match(op, ">>|<<|&|^|%or%")) { const char cop = op->str()[0]; std::string result; if (tok->str().find_first_of("uU") != std::string::npos) result = ShiftUInt(cop, tok, tok->tokAt(2)); else result = ShiftInt(cop, tok, tok->tokAt(2)); if (!result.empty()) { ret = true; tok->str(result); tok->deleteNext(2); continue; } } 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); ret = true; } 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(), "( %var% ) ;|)|,|]") || (Token::Match(tok->next(), "( %var% ) %cop%") && (tok->tokAt(2)->varId()>0 || !Token::Match(tok->tokAt(4), "[*&+-]")))) && !tok->isName() && tok->str() != ">" && tok->str() != "]" && tok->strAt(-1) != "operator" && !Token::simpleMatch(tok->previous(), "* )") && !Token::simpleMatch(tok->previous(), ") )") && !Token::Match(tok->tokAt(-2), "* %var% )") && !Token::Match(tok->tokAt(-2), "%type% ( ) ( %var%") && !Token::Match(tok, ") ( %var% ) ;") ) { tok->deleteNext(); tok = tok->next(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { tok->str(MathLib::toString(tok->str()[1] & 0xff)); } 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; for (; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") ++par; else if (tok2->str() == ")") { if (par == 0) break; --par; } else if (par == 0 && (Token::Match(tok2, "[,;?]"))) break; } if (Token::Match(tok2, "[);,?]")) { Token::eraseTokens(tok, tok2); ret = true; } continue; } if (tok->str() == "0") { if (Token::Match(tok->previous(), "[+-|] 0")) { tok = tok->previous(); if (Token::Match(tok->tokAt(-4), "[;{}] %var% = %var% [+-|] 0 ;") && tok->strAt(-3) == tok->previous()->str()) { tok = tok->tokAt(-3); tok->deleteNext(2); tok->deleteThis(); } tok->deleteNext(); tok->deleteThis(); ret = true; } else if (Token::Match(tok->previous(), "[=([,] 0 [+|]") || Token::Match(tok->previous(), "return|case 0 [+|]")) { tok->deleteNext(); tok->deleteThis(); ret = true; } else if (Token::Match(tok->previous(), "[=[(,] 0 * %var% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "[=[(,] 0 * %num% ,|]|)|;|%op%") || Token::Match(tok->previous(), "[=[(,] 0 * (") || Token::Match(tok->previous(), "return|case 0 *|&& %var% ,|:|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 *|&& %num% ,|:|;|%op%") || 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::simpleMatch(tok->previous(), "* 1") || Token::simpleMatch(tok, "1 *")) { if (tok->previous() && tok->previous()->isConstOp()) tok = tok->previous(); tok->deleteNext(); tok->deleteThis(); ret = true; } // Remove parentheses around number.. if (Token::Match(tok->tokAt(-2), "%any% ( %num% )") && !tok->tokAt(-2)->isName() && tok->strAt(-2) != ">") { tok = tok->previous(); tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::simpleMatch(tok->previous(), "( 0 ||") || Token::simpleMatch(tok->previous(), "|| 0 )") || Token::simpleMatch(tok->previous(), "( 0 |") || Token::simpleMatch(tok->previous(), "| 0 )") || Token::simpleMatch(tok->previous(), "( 1 &&") || Token::simpleMatch(tok->previous(), "&& 1 )")) { if (tok->previous()->isConstOp()) tok = tok->previous(); tok->deleteNext(); tok->deleteThis(); 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 if (cmp == ">") 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 { ret |= simplifyNumericCalculations(tok); } } return ret; } bool TemplateSimplifier::simplifyTemplateInstantiations( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, const Token *tok, 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; for (tok = tok->tokAt(2); tok && tok->str() != ">"; tok = tok->next()) { if (Token::Match(tok, "%var% ,|>")) typeParametersInDeclaration.push_back(tok); } // bail out if the end of the file was reached if (!tok) return false; // get the position of the template name int namepos = TemplateSimplifier::getTemplateNamePosition(tok); if (namepos == -1) { // debug message that we bail out.. if (_settings->debugwarnings && 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 amountOftemplateInstantiations = templateInstantiations.size(); unsigned int recursiveCount = 0; bool instantiated = false; for (std::list::const_iterator iter2 = templateInstantiations.begin(); iter2 != templateInstantiations.end(); ++iter2) { if (amountOftemplateInstantiations != templateInstantiations.size()) { amountOftemplateInstantiations = templateInstantiations.size(); simplifyCalculations(tokenlist.front()); ++recursiveCount; if (recursiveCount > 100) { // bail out.. break; } } Token * const tok2 = *iter2; if (tok2->str() != name) continue; if (Token::Match(tok2->previous(), "[;{}=]") && !TemplateSimplifier::instantiateMatch(*iter2, name, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %var%")) continue; // New type.. std::vector typesUsedInTemplateInstantiation; std::string typeForNewNameStr; std::string templateMatchPattern(name + " < "); 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 (tok3->str() == "(" || tok3->str() == "[") { typeForNewNameStr.clear(); break; } if (!tok3->next()) { typeForNewNameStr.clear(); break; } if (Token::Match(tok3->tokAt(-2), "[<,] %var% <") && templateParameters(tok3) > 0) ++indentlevel; else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) --indentlevel; else if (indentlevel > 0 && tok3->str() == ">>") { if (indentlevel == 1) { templateMatchPattern += '>'; typeForNewNameStr += '>'; break; } indentlevel -= 2; } templateMatchPattern += (tok3->str() == ">>") ? std::string("> >") : tok3->str(); templateMatchPattern += ' '; if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) typesUsedInTemplateInstantiation.push_back(tok3); // add additional type information if (tok3->str() != "class") { if (tok3->isUnsigned()) typeForNewNameStr += "unsigned"; else if (tok3->isSigned()) typeForNewNameStr += "signed"; if (tok3->isLong()) typeForNewNameStr += "long"; typeForNewNameStr += tok3->str(); } } templateMatchPattern += ">"; const std::string typeForNewName(typeForNewNameStr); if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) { if (_settings->debugwarnings && errorlogger) { std::list callstack(1, tok); errorlogger->reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug", "Failed to instantiate template. The checking continues anyway.", false)); } if (typeForNewName.empty()) continue; break; } // New classname/funcname.. const std::string newName(name + "<" + typeForNewName + ">"); if (expandedtemplates.find(newName) == expandedtemplates.end()) { expandedtemplates.insert(newName); TemplateSimplifier::expandTemplate(tokenlist, tok,name,typeParametersInDeclaration,newName,typesUsedInTemplateInstantiation,templateInstantiations); instantiated = true; } // Replace all these template usages.. std::list< std::pair > removeTokens; for (Token *tok4 = tok2; tok4; tok4 = tok4->next()) { if (Token::simpleMatch(tok4, templateMatchPattern.c_str())) { Token * tok5 = tok4->tokAt(2); unsigned int typeCountInInstantiation = 1U; // There is always at least one type const Token *typetok = (!typesUsedInTemplateInstantiation.empty()) ? typesUsedInTemplateInstantiation[0] : 0; unsigned int indentlevel5 = 0; // indentlevel for tok5 while (tok5 && (indentlevel5 > 0 || tok5->str() != ">")) { if (tok5->str() == "<" && templateParameters(tok5) > 0) ++indentlevel5; else if (indentlevel5 > 0 && Token::Match(tok5, "> [,>]")) --indentlevel5; else if (indentlevel5 == 0) { if (tok5->str() != ",") { if (!typetok || tok5->isUnsigned() != typetok->isUnsigned() || tok5->isSigned() != typetok->isSigned() || tok5->isLong() != typetok->isLong()) { break; } typetok = typetok ? typetok->next() : 0; } else { typetok = (typeCountInInstantiation < typesUsedInTemplateInstantiation.size()) ? typesUsedInTemplateInstantiation[typeCountInInstantiation] : 0; ++typeCountInInstantiation; } } tok5 = tok5->next(); } // matching template usage => replace tokens.. // Foo < int > => Foo if (tok5 && tok5->str() == ">" && typeCountInInstantiation == typesUsedInTemplateInstantiation.size()) { tok4->str(newName); for (Token *tok6 = tok4->next(); tok6 != tok5; tok6 = tok6->next()) { if (tok6->isName()) templateInstantiations.remove(tok6); } removeTokens.push_back(std::pair(tok4, tok5->next())); } tok4 = tok5; if (!tok4) break; } } while (!removeTokens.empty()) { Token::eraseTokens(removeTokens.back().first, removeTokens.back().second); removeTokens.pop_back(); } } // Template has been instantiated .. then remove the template declaration return instantiated; } void TemplateSimplifier::simplifyTemplates( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, 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 templates(TemplateSimplifier::getTemplateDeclarations(tokenlist.front(), _codeWithTemplates)); // Locate possible instantiations of templates.. std::list templateInstantiations(TemplateSimplifier::getTemplateInstantiations(tokenlist.front())); // No template instantiations? Then return. if (templateInstantiations.empty()) return; // Template arguments with default values TemplateSimplifier::useDefaultArgumentValues(templates, &templateInstantiations); // expand templates //bool done = false; //while (!done) { //done = true; std::list templates2; for (std::list::reverse_iterator iter1 = templates.rbegin(); iter1 != templates.rend(); ++iter1) { bool instantiated = TemplateSimplifier::simplifyTemplateInstantiations(tokenlist, errorlogger, _settings, *iter1, templateInstantiations, expandedtemplates); if (instantiated) templates2.push_back(*iter1); } for (std::list::iterator it = templates2.begin(); it != templates2.end(); ++it) { std::list::iterator it1 = std::find(templates.begin(), templates.end(), *it); if (it1 != templates.end()) { templates.erase(it1); removeTemplate(*it); } } } } cppcheck-1.66/lib/templatesimplifier.h000066400000000000000000000141551236713773000200630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include #include "config.h" class Token; class TokenList; class ErrorLogger; class Settings; /// @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); /** * @return 0 if there are no syntax errors or return token which identifies * the location of syntax error. */ static const Token* hasComplicatedSyntaxErrorsInTemplates(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); /** * Get template declarations * @return list of template declarations */ static std::list getTemplateDeclarations(Token *tokens, bool &codeWithTemplates); /** * Get template instantiations * @return list of template instantiations */ static std::list getTemplateInstantiations(Token *tokens); /** * 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); /** * Match template declaration/instantiation * @param instance template instantiation * @param name name of template * @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::string &name, 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); static void expandTemplate( TokenList& tokenlist, const Token *tok, const std::string &name, std::vector &typeParametersInDeclaration, const std::string &newName, std::vector &typesUsedInTemplateInstantiation, std::list &templateInstantiations); /** * 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 tok token where the template declaration begins * @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 Token *tok, std::list &templateInstantiations, std::set &expandedtemplates); /** * Simplify templates * @param tokenlist token list * @param errorlogger error logger * @param _settings settings * @param _codeWithTemplates output parameter that is set if code contains templates */ static void simplifyTemplates( TokenList& tokenlist, ErrorLogger* errorlogger, const Settings *_settings, 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); }; /// @} //--------------------------------------------------------------------------- #endif // templatesimplifierH cppcheck-1.66/lib/timer.cpp000066400000000000000000000063711236713773000156400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 /* 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.66/lib/timer.h000066400000000000000000000045141236713773000153020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include "config.h" 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 = NULL); ~Timer(); void Stop(); private: 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.66/lib/token.cpp000066400000000000000000001073731236713773000156440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "check.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include Token::Token(Token **t) : tokensBack(t), _next(0), _previous(0), _link(0), _scope(0), _function(0), // Initialize whole union _varId(0), _fileIndex(0), _linenr(0), _progressValue(0), _type(eNone), _flags(0), _astOperand1(nullptr), _astOperand2(nullptr), _astParent(nullptr), _originalName(nullptr) { } Token::~Token() { delete _originalName; } void Token::update_property_info() { if (!_str.empty()) { if (_str == "true" || _str == "false") _type = eBoolean; else if (_str[0] == '_' || std::isalpha((unsigned char)_str[0])) { // Name if (_varId) _type = eVariable; else if (_type != eVariable && _type != eFunction && _type != eType && _type != eKeyword) _type = eName; } else if (std::isdigit((unsigned char)_str[0]) || (_str.length() > 1 && _str[0] == '-' && std::isdigit((unsigned char)_str[1]))) _type = eNumber; else if (_str.length() > 1 && _str[0] == '"' && _str[_str.length()-1] == '"') _type = eString; else if (_str.length() > 1 && _str[0] == '\'' && _str[_str.length()-1] == '\'') _type = eChar; else if (_str == "=" || _str == "<<=" || _str == ">>=" || (_str.size() == 2U && _str[1] == '=' && std::strchr("+-*/%&^|", _str[0]))) _type = eAssignmentOp; else if (_str.size() == 1 && _str.find_first_of(",[]()?:") != std::string::npos) _type = eExtendedOp; else if (_str=="<<" || _str==">>" || (_str.size()==1 && _str.find_first_of("+-*/%") != std::string::npos)) _type = eArithmeticalOp; else if (_str.size() == 1 && _str.find_first_of("&|^~") != std::string::npos) _type = eBitOp; else if (_str.size() <= 2 && (_str == "&&" || _str == "||" || _str == "!")) _type = eLogicalOp; else if (_str.size() <= 2 && !_link && (_str == "==" || _str == "!=" || _str == "<" || _str == "<=" || _str == ">" || _str == ">=")) _type = eComparisonOp; else if (_str.size() == 2 && (_str == "++" || _str == "--")) _type = eIncDecOp; else if (_str.size() == 1 && (_str.find_first_of("{}") != std::string::npos || (_link && _str.find_first_of("<>") != std::string::npos))) _type = eBracket; else _type = eOther; } else { _type = eNone; } update_property_isStandardType(); } void Token::update_property_isStandardType() { isStandardType(false); if (_str.size() < 3) return; static const char * const stdtype[] = { "bool", "char", "char16_t", "char32_t", "double", "float", "int", "long", "short", "size_t", "void", "wchar_t"}; if (std::binary_search(stdtype, stdtype + sizeof(stdtype) / sizeof(stdtype[0]), _str)) { isStandardType(true); _type = 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(_type == eString); return _str.substr(1, _str.length() - 2); } void Token::deleteNext(unsigned long index) { while (_next && index--) { Token *n = _next; _next = n->next(); delete n; } if (_next) _next->previous(this); else if (tokensBack) *tokensBack = this; } void Token::deleteThis() { if (_next) { // Copy next to this and delete next _str = _next->_str; _type = _next->_type; _flags = _next->_flags; _varId = _next->_varId; _fileIndex = _next->_fileIndex; _linenr = _next->_linenr; _link = _next->_link; _scope = _next->_scope; _function = _next->_function; _variable = _next->_variable; if (_next->_originalName) { _originalName = _next->_originalName; _next->_originalName = nullptr; } values = _next->values; if (_link) _link->link(this); deleteNext(); } else if (_previous && _previous->_previous) { // Copy previous to this and delete previous _str = _previous->_str; _type = _previous->_type; _flags = _previous->_flags; _varId = _previous->_varId; _fileIndex = _previous->_fileIndex; _linenr = _previous->_linenr; _link = _previous->_link; _scope = _previous->_scope; _function = _previous->_function; _variable = _previous->_variable; if (_previous->_originalName) { _originalName = _previous->_originalName; _previous->_originalName = nullptr; } values = _previous->values; if (_link) _link->link(this); 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; int num = std::abs(index); while (num > 0 && tok) { if (index > 0) tok = tok->next(); else tok = tok->previous(); --num; } 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, bool emptyStringFound, 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': // TODO: %var% should match only for // variables that have varId != 0, but that needs a lot of // work, before that change can be made. // Any symbolname.. if (haystack[3] == '%') { // %var% haystack += 4; if (tok->isName()) 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%) { haystack += 4; return 1; } case 'n': // Number (%num%) { haystack += 4; if (tok->isNumber()) return 1; } break; case 'c': { haystack += 1; // Character (%char%) if (haystack[0] == 'h') { haystack += 4; if (tok->type() == 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->type() == 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->type() == Token::eBitOp && tok->str() == "|") return 1; } } // Oror (%oror%) else { haystack += 4; if (tok->type() == Token::eLogicalOp && tok->str() == "||") return 1; } } break; default: //unknown %cmd%, abort std::abort(); } if (*haystack == '|') haystack += 1; else if (*haystack == ' ' || *haystack == '\0') return emptyStringFound ? 0 : -1; else return -1; return 0xFFFF; } int Token::multiCompare(const Token *tok, const char *haystack, unsigned int varid) { bool emptyStringFound = false; 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, emptyStringFound, varid); if (ret < 2) return ret; } else if (*haystack == '|') { if (*needlePointer == 0) { // If needle is at the end, we have a match. return 1; } else if (needlePointer == needle) { // If needlePointer was not increased at all, we had a empty // string in the haystack emptyStringFound = true; } 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 emptyStringFound ? 0 : -1; } ++haystack; } } if (*needlePointer == '\0') return 1; // If empty string was found earlier from the haystack if (emptyStringFound) return 0; 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 = static_cast(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 0; if (*str == c) return str; ++str; } } int Token::firstWordLen(const char *str) { int len = 0; for (;;) { if (*str == ' ' || *str == 0) break; ++len; ++str; } return len; } 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); std::size_t len = 0; const std::string strValue(tok->strValue()); const char *str = strValue.c_str(); while (*str) { if (*str == '\\') { ++str; // string ends at '\0' if (*str == '0') break; } ++str; ++len; } return len; } std::size_t Token::getStrSize(const Token *tok) { assert(tok != nullptr && tok->type() == 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); const std::string strValue(tok->strValue()); const char *str = strValue.c_str(); while (*str) { if (index == 0) { std::string ret; if (*str == '\\') { ret = *str; ++str; } ret += *str; return ret; } if (*str == '\\') ++str; ++str; --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->str() == "(" || tok->str() == "{" || tok->str() == "[") tok = tok->link(); else if (tok->str() == "<" && tok->link()) tok = tok->link(); else if (tok->str() == ")" || tok->str() == ";") return 0; } return 0; } const Token * Token::findClosingBracket() const { const Token *closing = nullptr; if (_str == "<") { unsigned int depth = 0; for (closing = this; closing != nullptr; closing = closing->next()) { if (closing->str() == "{" || closing->str() == "[" || closing->str() == "(") closing = closing->link(); else if (closing->str() == "}" || closing->str() == "]" || closing->str() == ")" || closing->str() == ";" || closing->str() == "=") break; else if (closing->str() == "<") ++depth; else if (closing->str() == ">") { if (--depth == 0) break; } else if (closing->str() == ">>") { if (--depth == 0) break; if (--depth == 0) break; } } } return closing; } Token * Token::findClosingBracket() { // return value of const function return const_cast(const_cast(this)->findClosingBracket()); } //--------------------------------------------------------------------------- const Token *Token::findsimplematch(const Token *tok, const char pattern[]) { for (; tok; tok = tok->next()) { if (Token::simpleMatch(tok, pattern)) return tok; } return 0; } const Token *Token::findsimplematch(const Token *tok, const char pattern[], const Token *end) { for (; tok && tok != end; tok = tok->next()) { if (Token::simpleMatch(tok, pattern)) return tok; } return 0; } const Token *Token::findmatch(const Token *tok, const char pattern[], unsigned int varId) { for (; tok; tok = tok->next()) { if (Token::Match(tok, pattern, varId)) return tok; } return 0; } const Token *Token::findmatch(const Token *tok, const char pattern[], const Token *end, unsigned int varId) { for (; tok && tok != end; tok = tok->next()) { if (Token::Match(tok, pattern, varId)) return tok; } return 0; } void Token::insertToken(const std::string &tokenStr, 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); 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::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, 0, 0) << 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, 0) << std::endl; } void Token::stringify(std::ostream& os, bool varid, bool attributes) const { if (attributes) { if (isUnsigned()) os << "unsigned "; else if (isSigned()) os << "signed "; if (isLong()) { if (_type == eString || _type == eChar) os << "L"; else os << "long "; } } if (isExpandedMacro()) os << "$"; 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; int fileInd = files ? -1 : static_cast(_fileIndex); std::map lineNumbers; for (const Token *tok = this; tok != end; tok = tok->next()) { bool fileChange = false; if (static_cast(tok->_fileIndex) != fileInd) { if (fileInd != -1) { lineNumbers[fileInd] = tok->_fileIndex; } fileInd = static_cast(tok->_fileIndex); if (files) { ret << "\n\n##file "; if (fileNames && fileNames->size() > tok->_fileIndex) ret << fileNames->at(tok->_fileIndex); else ret << fileInd; } lineNumber = lineNumbers[fileInd]; fileChange = true; } if (linebreaks && (lineNumber != tok->linenr() || fileChange)) { if (lineNumber+4 < tok->linenr() && fileInd == static_cast(tok->_fileIndex)) { ret << '\n' << lineNumber+1 << ":\n|\n"; ret << tok->linenr()-1 << ":\n"; 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); // print token if (tok->next() != end && (!linebreaks || (tok->next()->linenr() <= tok->linenr() && tok->next()->fileIndex() == tok->fileIndex()))) ret << ' '; } if (linebreaks && files) ret << '\n'; return ret.str(); } std::string Token::stringifyList(const Token* end, bool attributes) const { return stringifyList(false, attributes, false, false, false, 0, end); } std::string Token::stringifyList(bool varid) const { return stringifyList(varid, false, true, true, true, 0, 0); } void Token::astOperand1(Token *tok) { if (_astOperand1) _astOperand1->_astParent = nullptr; // goto parent operator if (tok) { while (tok->_astParent) tok = tok->_astParent; tok->_astParent = this; } _astOperand1 = tok; } void Token::astOperand2(Token *tok) { if (_astOperand2) _astOperand2->_astParent = nullptr; // goto parent operator if (tok) { while (tok->_astParent) 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; } static bool isUnaryPreOp(const Token *op) { if (!op->astOperand1() || op->astOperand2()) return false; if (!Token::Match(op, "++|--")) return true; const Token *tok = op->astOperand1(); for (int distance = 1; distance < 10; distance++) { if (tok == op->tokAt(-distance)) return false; if (tok == op->tokAt(distance)) return true; } return false; // <- guess } std::string Token::expressionString() const { const Token * const top = this; const Token *start = top; while (start->astOperand1() && start->astOperand2()) start = start->astOperand1(); const Token *end = top; while (end->astOperand1() && (end->astOperand2() || isUnaryPreOp(end))) { if (Token::Match(end,"(|[")) { end = end->link(); break; } end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); } std::string ret; for (const Token *tok = start; tok && tok != end; tok = tok->next()) { ret += tok->str(); if (Token::Match(tok, "%var%|%num% %var%|%num%")) ret += " "; } return ret + end->str(); } 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 { bool title = false; bool print = true; for (const Token *tok = this; tok; tok = tok->next()) { if (print && tok->_astOperand1) { if (!title && !xml) out << "\n\n##AST" << std::endl; title = true; if (xml) { out << "scope() << "\" fileIndex=\"" << tok->fileIndex() << "\" linenr=\"" << tok->linenr() << "\">" << std::endl; astStringXml(tok->astTop(), 2U, out); out << "" << std::endl; } else if (verbose) out << tok->astTop()->astStringVerbose(0,0) << std::endl; else out << tok->astTop()->astString(" ") << std::endl; print = false; if (tok->str() == "(") tok = tok->link(); } if (Token::Match(tok, "[;{}]")) print = true; } } 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 = _str + "\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.empty()) 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() << ":{"; for (std::list::const_iterator it=tok->values.begin(); it!=tok->values.end(); ++it) { if (xml) { out << " intvalue << "\""; if (it->condition) { out << " condition-line=\"" << it->condition->linenr() << '\"'; } out << "/>" << std::endl; } else { out << (it == tok->values.begin() ? "" : ",") << it->intvalue; } } if (xml) 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 { const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { if (it->intvalue <= val) { if (!ret || ret->inconclusive || (ret->condition && !it->inconclusive)) ret = &(*it); if (!ret->inconclusive && !ret->condition) break; } } if (settings && ret) { if (ret->inconclusive && !settings->inconclusive) return nullptr; if (ret->condition && !settings->isEnabled("warning")) return nullptr; } return ret; } const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Settings *settings) const { const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { if (it->intvalue >= val) { if (!ret || ret->inconclusive || (ret->condition && !it->inconclusive)) ret = &(*it); if (!ret->inconclusive && !ret->condition) break; } } if (settings && ret) { if (ret->inconclusive && !settings->inconclusive) return nullptr; if (ret->condition && !settings->isEnabled("warning")) return nullptr; } return ret; } 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; } cppcheck-1.66/lib/token.h000066400000000000000000000654661236713773000153170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include #include #include "config.h" #include "valueflow.h" #include "mathlib.h" class Scope; class Function; class Variable; class Settings; /// @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 (linenr, fileIndex) * - 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, // Literals: Number, String, Character, User defined literal (C++11) 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 **tokensBack); ~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); /** * @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(static_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(static_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 * - "%var%" any token which is a name or type e.g. "hello" or "int" * - "%type%" Anything that can be a variable type, e.g. "int", but not "delete". * - "%num%" Any numeric token, e.g. "23" * - "%bool%" true or false * - "%char%" Any token enclosed in '-character. * - "%comp%" Any token such that isComparisonOp() returns true. * - "%str%" Any token starting with "-character (C-string). * - "%varid%" Match with parameter varid * - "%op%" Any token such that isOp() returns true. * - "%cop%" Any token such that isConstOp() returns true. * - "%or%" A bitwise-or operator '|' * - "%oror%" A logical-or operator '||' * - "[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% * but it is not recommended to put such an %%cmd% as the first pattern. * * It's possible to use multi-compare patterns with all the other %%cmds%, * except for %%varid%, and normal names, but the %%cmds% should be put as * the first patterns in the list, then the normal names. * For example: "%var%|%num%|)" means yes to a variable, a number or ')'. * * @todo Make it possible to use the %%cmds% and the normal names in the * multicompare list without an order. * * 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); Type type() const { return _type; } void type(Type t) { _type = t; } void isKeyword(bool kwd) { if (kwd) _type = eKeyword; else if (_type == eKeyword) _type = eName; } bool isKeyword() const { return _type == eKeyword; } bool isName() const { return _type == eName || _type == eType || _type == eVariable || _type == eFunction || _type == eKeyword || _type == eBoolean; // TODO: "true"/"false" aren't really a name... } bool isUpperCaseName() const; bool isLiteral() const { return _type == eNumber || _type == eString || _type == eChar || _type == eBoolean || _type == eLiteral; } bool isNumber() const { return _type == eNumber; } bool isOp() const { return (isConstOp() || isAssignmentOp() || _type == eIncDecOp); } bool isConstOp() const { return (isArithmeticalOp() || _type == eLogicalOp || _type == eComparisonOp || _type == eBitOp); } bool isExtendedOp() const { return isConstOp() || _type == eExtendedOp; } bool isArithmeticalOp() const { return _type == eArithmeticalOp; } bool isComparisonOp() const { return _type == eComparisonOp; } bool isAssignmentOp() const { return _type == eAssignmentOp; } bool isBoolean() const { return _type == eBoolean; } 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 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 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 isAttributeNothrow() const { return getFlag(fIsAttributeNothrow); } void isAttributeNothrow(bool value) { setFlag(fIsAttributeNothrow, value); } bool isDeclspecNothrow() const { return getFlag(fIsDeclspecNothrow); } void isDeclspecNothrow(bool value) { setFlag(fIsDeclspecNothrow, value); } static const Token *findsimplematch(const Token *tok, const char pattern[]); static const Token *findsimplematch(const Token *tok, const char pattern[], const Token *end); static const Token *findmatch(const Token *tok, const char pattern[], unsigned int varId = 0); static const Token *findmatch(const Token *tok, const char pattern[], const Token *end, unsigned int varId = 0); static Token *findsimplematch(Token *tok, const char pattern[]) { return const_cast(findsimplematch(static_cast(tok), pattern)); } static Token *findsimplematch(Token *tok, const char pattern[], const Token *end) { return const_cast(findsimplematch(static_cast(tok), pattern, end)); } static Token *findmatch(Token *tok, const char pattern[], unsigned int varId = 0) { return const_cast(findmatch(static_cast(tok), pattern, varId)); } static Token *findmatch(Token *tok, const char pattern[], const Token *end, unsigned int varId = 0) { return const_cast(findmatch(static_cast(tok), 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 needle Current token * @param haystack e.g. "one|two" or "|one|two" * @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 *needle, const char *haystack, unsigned int varid); unsigned int linenr() const { return _linenr; } void linenr(unsigned int lineNumber) { _linenr = lineNumber; } unsigned int fileIndex() const { return _fileIndex; } void fileIndex(unsigned int indexOfFile) { _fileIndex = indexOfFile; } 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 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, bool prepend=false); void insertToken(const std::string &tokenStr, const std::string &originalNameStr, bool prepend=false); Token *previous() const { return _previous; } unsigned int varId() const { return _varId; } void varId(unsigned int id) { _varId = id; if (id != 0) _type = eVariable; 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. */ void stringify(std::ostream& os, bool varid, bool attributes) 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 = 0, const Token* end = 0) 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) _type = eFunction; else if (_type == eFunction) _type = eName; } /** * @return a pointer to the Function associated with this token. */ const Function *function() const { return _type == eFunction ? _function : 0; } /** * Associate this token with given variable * @param v Variable to be associated */ void variable(const Variable *v) { _variable = v; if (v || _varId) _type = eVariable; else if (_type == eVariable) _type = eName; } /** * @return a pointer to the variable associated with this token. */ const Variable *variable() const { return _type == eVariable ? _variable : 0; } /** * 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. Returns 0, if there is no next argument */ Token* nextArgument() 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; } /** * Sets the original name. */ template void originalName(T&& name) { if (!_originalName) _originalName = new std::string(name); else *_originalName = name; } /** Values of token */ std::list values; const ValueFlow::Value * getValue(const MathLib::bigint val) const { std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { if (it->intvalue == val) return &(*it); } return NULL; } const ValueFlow::Value * getMaxValue(bool condition) const { const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) { if ((!ret || it->intvalue > ret->intvalue) && ((it->condition != NULL) == condition)) ret = &(*it); } return ret; } const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings *settings) const; const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings *settings) const; private: void next(Token *nextToken) { _next = nextToken; } void previous(Token *previousToken) { _previous = previousToken; } /** * 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); /** * Works almost like strlen() except * if str has empty space ' ' character, that character is handled * as if it were '\\0' */ static int firstWordLen(const char *str); std::string _str; Token *_next; Token *_previous; Token *_link; // symbol database information const Scope *_scope; union { const Function *_function; const Variable *_variable; }; unsigned int _varId; unsigned int _fileIndex; unsigned int _linenr; /** * A value from 0-100 that provides a rough idea about where in the token * list this token is located. */ unsigned int _progressValue; Type _type; enum { fIsUnsigned = (1 << 0), fIsSigned = (1 << 1), fIsPointerCompare = (1 << 2), fIsLong = (1 << 3), fIsStandardType = (1 << 4), fIsExpandedMacro = (1 << 5), fIsAttributeConstructor = (1 << 6), // __attribute__((constructor)) __attribute__((constructor(priority))) fIsAttributeDestructor = (1 << 7), // __attribute__((destructor)) __attribute__((destructor(priority))) fIsAttributeUnused = (1 << 8), // __attribute__((unused)) fIsAttributePure = (1 << 9), // __attribute__((pure)) fIsAttributeConst = (1 << 10), // __attribute__((const)) fIsAttributeNothrow = (1 << 11), // __attribute__((nothrow)) fIsDeclspecNothrow = (1 << 12) // __declspec(nothrow) }; 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 bool((_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; 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 = NULL; } 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.66/lib/tokenize.cpp000066400000000000000000014307641236713773000163600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "mathlib.h" #include "settings.h" #include "check.h" #include "path.h" #include "symboldatabase.h" #include "templatesimplifier.h" #include "timer.h" #include #include #include #include #include #include //--------------------------------------------------------------------------- Tokenizer::Tokenizer() : list(0), _settings(0), _errorLogger(0), _symbolDatabase(0), _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(0), _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->type() == Token::eString) return static_cast(Token::getStrLength(type) + 1); std::map::const_iterator it = _typeSize.find(type->str()); if (it == _typeSize.end()) return 0; 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; } //--------------------------------------------------------------------------- Token *Tokenizer::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->type(tok->type()); tok2->isUnsigned(tok->isUnsigned()); tok2->isSigned(tok->isSigned()); tok2->isPointerCompare(tok->isPointerCompare()); tok2->isLong(tok->isLong()); tok2->isExpandedMacro(tok->isExpandedMacro()); tok2->isAttributeConstructor(tok->isAttributeConstructor()); tok2->isAttributeDestructor(tok->isAttributeDestructor()); tok2->isAttributeUnused(tok->isAttributeUnused()); tok2->isAttributePure(tok->isAttributePure()); tok2->isAttributeConst(tok->isAttributeConst()); tok2->isAttributeNothrow(tok->isAttributeNothrow()); tok2->isDeclspecNothrow(tok->isDeclspecNothrow()); tok2->varId(tok->varId()); // Check for links and fix them up if (tok2->str() == "(" || tok2->str() == "[" || tok2->str() == "{") links.push(tok2); else if (tok2->str() == ")" || tok2->str() == "]" || tok2->str() == "}") { 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; } //--------------------------------------------------------------------------- void Tokenizer::duplicateTypedefError(const Token *tok1, const Token *tok2, const std::string &type) const { if (tok1 && !(_settings->isEnabled("style") && _settings->inconclusive)) return; std::list locationList; locationList.push_back(tok1); locationList.push_back(tok2); const std::string tok2_str = tok2 ? tok2->str() : std::string("name"); reportError(locationList, Severity::style, "variableHidingTypedef", std::string("The " + type + " '" + tok2_str + "' hides a typedef with the same name."), true); } void Tokenizer::duplicateDeclarationError(const Token *tok1, const Token *tok2, const std::string &type) const { if (tok1 && !(_settings->isEnabled("style"))) return; std::list locationList; locationList.push_back(tok1); locationList.push_back(tok2); const std::string tok2_str = tok2 ? tok2->str() : std::string("name"); reportError(locationList, Severity::style, "unnecessaryForwardDeclaration", std::string("The " + type + " '" + tok2_str + "' forward declaration is unnecessary. Type " + type + " is already declared earlier.")); } // check if this statement is a duplicate definition bool Tokenizer::duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef, bool undefinedStruct) 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() == "[") { 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->next(); } 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; duplicateTypedefError(*tokPtr, name, "template instantiation"); *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")) { duplicateTypedefError(*tokPtr, name, "function parameter"); // 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) { duplicateTypedefError(*tokPtr, name, "template parameter"); *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") { duplicateTypedefError(*tokPtr, name, "typedef"); return true; } else if (tok->previous()->str() == "enum") { duplicateTypedefError(*tokPtr, name, "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() == "{") { if (!undefinedStruct) duplicateTypedefError(*tokPtr, name, "struct"); return true; } else if (Token::Match(tok->next(), ")|*")) { return true; } else if (tok->next()->str() == name->str()) { return true; } else if (tok->next()->str() != ";") { duplicateTypedefError(*tokPtr, name, "struct"); return true; } else { // forward declaration after declaration duplicateDeclarationError(*tokPtr, name, "struct"); return false; } } else if (tok->previous()->str() == "union") { if (tok->next()->str() != ";") { duplicateTypedefError(*tokPtr, name, "union"); return true; } else { // forward declaration after declaration duplicateDeclarationError(*tokPtr, name, "union"); return false; } } else if (tok->previous()->str() == "class") { if (tok->next()->str() != ";") { duplicateTypedefError(*tokPtr, name, "class"); return true; } else { // forward declaration after declaration duplicateDeclarationError(*tokPtr, name, "class"); return false; } } tok = tok->previous(); } duplicateTypedefError(*tokPtr, name, "variable"); 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) 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; } 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 */ static Token *processFunc(Token *tok2, bool inOperator) { 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, "%var% ::")) tok2 = tok2->tokAt(2); if (!tok2) return nullptr; if (tok2->str() == "(" && 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()->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; #ifdef MAXTIME if (std::time(0) > maxtime) return; #endif if (goback) { //jump back once, see the comment at the end of the function goback = false; tok = tok->previous(); } // Skip typedefs inside parentheses (#2453 and #4002) if (tok->str() == "(" && tok->strAt(1) == "typedef") { tok = tok->next(); continue; } if (Token::Match(tok, "class|struct|namespace %any%") && (!tok->previous() || (tok->previous() && tok->previous()->str() != "enum"))) { isNamespace = (tok->str() == "namespace"); hasClass = true; className = tok->next()->str(); continue; } else if (hasClass && tok->str() == ";") { hasClass = false; continue; } else if (hasClass && tok->str() == "{") { Space info; info.isNamespace = isNamespace; info.className = className; info.classEnd = tok->link(); spaceInfo.push_back(info); hasClass = false; continue; } else if (!spaceInfo.empty() && tok->str() == "}" && spaceInfo.back().classEnd == tok) { spaceInfo.pop_back(); continue; } else if (tok->str() != "typedef") 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::Match(tok->next(), "const| struct|enum|union|class {")) { 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 */ bool undefinedStruct = false; if (Token::Match(tok, "typedef enum|struct %type% %type% ;") && tok->strAt(2) == tok->strAt(3)) { if (tok->next()->str() == "enum") { tok->deleteNext(3); tok->deleteThis(); if (tok->next()) tok->deleteThis(); //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } else { const std::string pattern("struct " + tok->strAt(2) + " {|:"); const Token *tok2 = Token::findmatch(list.front(), pattern.c_str(), tok); if (!tok2) undefinedStruct = true; } } Token *typeName; std::list pointers; 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 (!tok->next()) { syntaxError(tok); return; } if (tok->next()->str() == "::" || Token::Match(tok->next(), "%type%")) { typeStart = tok->next(); while (Token::Match(tokOffset, "const|signed|unsigned|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); return; } // check for template if (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(); } // 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); return; } 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() == "(") { // 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; } // unhandled function pointer, skip it and continue // TODO: handle such typedefs. See ticket #3314 else if (Token::Match(tokOffset, "( %type% ::") && Token::Match(tokOffset->link()->tokAt(-3), ":: * %var% ) (")) { 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(tokOffset, "( * %var% ) (")) { // name token wasn't a name, it was part of the type typeEnd = typeEnd->next(); functionPtr = true; tokOffset = tokOffset->next(); funcStart = tokOffset; funcEnd = tokOffset; tokOffset = tokOffset->tokAt(3); typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); tok = argEnd->next(); } // function else if (Token::Match(tokOffset->link(), ") const| ;|,")) { 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); return; } } // 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() == "(" && Token::Match(tokOffset->link()->previous(), "%type% ) (") && Token::Match(tokOffset->link()->next()->link(), ") const|volatile|;")) || (Token::simpleMatch(tokOffset, "( (") && Token::Match(tokOffset->next()->link()->previous(), "%type% ) (") && Token::Match(tokOffset->next()->link()->next()->link(), ") const|volatile| ) ;|,")) || (Token::simpleMatch(tokOffset, "( * (") && 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(); 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->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(); argFuncRetStart = argEnd->tokAt(2); argFuncRetEnd = argFuncRetStart->link(); 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); argFuncRetEnd = argFuncRetStart->link(); tok = argFuncRetEnd->next(); } else if (Token::Match(tokOffset, "( * ( %type% ) (")) { functionRetFuncPtr = true; tokOffset = tokOffset->tokAt(5); typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); argFuncRetStart = argEnd->tokAt(2); argFuncRetEnd = argFuncRetStart->link(); tok = argFuncRetEnd->next(); } // pointer/reference to array else if (Token::Match(tokOffset, "( *|& %type% ) [")) { ptrToArray = (tokOffset->next()->str() == "*"); refToArray = (tokOffset->next()->str() == "&"); tokOffset = tokOffset->tokAt(2); typeName = tokOffset; arrayStart = tokOffset->tokAt(2); arrayEnd = arrayStart->link(); tok = arrayEnd->next(); } // pointer to class member else if (Token::Match(tokOffset, "( %type% :: * %type% ) ;")) { tokOffset = tokOffset->tokAt(2); namespaceStart = tokOffset->previous(); namespaceEnd = tokOffset; ptrMember = true; tokOffset = tokOffset->tokAt(2); typeName = tokOffset; tok = tokOffset->tokAt(2); } // unhandled typedef, skip it and continue else { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } bool done = false; bool ok = true; while (!done) { std::string pattern = typeName->str(); int scope = 0; bool inScope = true; bool exitThisScope = false; int exitScope = 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; // 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 { --scope; if (scope < 0) inScope = false; if (exitThisScope) { if (scope < exitScope) exitThisScope = false; } } } // check for operator typedef /** @todo add support for multi-token operators */ else if (tok2->str() == "operator" && tok2->next()->str() == typeName->str() && 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 member functions else if (Token::Match(tok2, ") const| {")) { const Token *func = tok2->link()->previous(); if (!func) continue; if (func->previous()) { // Ticket #4239 /** @todo add support for multi-token operators */ if (func->previous()->str() == "operator") func = func->previous(); // 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 namespace else if (Token::Match(tok2, "namespace %any% {")) { if (classLevel < spaceInfo.size() && spaceInfo[classLevel].isNamespace && spaceInfo[classLevel].className == tok2->next()->str()) { ++classLevel; pattern.clear(); for (std::size_t i = classLevel; i < spaceInfo.size(); ++i) pattern += (spaceInfo[i].className + " :: "); pattern += typeName->str(); } ++scope; } // check for entering a new scope else if (tok2->str() == "{") { // keep track of scopes within member function if (inMemberFunc) ++memberScope; ++scope; } // check for typedef that can be substituted else if (Token::Match(tok2, pattern.c_str()) || (inMemberFunc && tok2->str() == typeName->str())) { std::string pattern1; // member function class variables don't need qualification if (inMemberFunc && tok2->str() == typeName->str()) pattern1 = tok2->str(); else pattern1 = pattern; if (pattern1.find("::") != std::string::npos) { // has a "something ::" if (tok2->strAt(-1) == "::") { tok2->tokAt(-2)->deleteNext(); globalScope = true; } for (std::size_t i = classLevel; i < spaceInfo.size(); ++i) { tok2->deleteNext(2); } simplifyType = true; } else if ((inScope && !exitThisScope) || inMemberFunc) { if (tok2->strAt(-1) == "::") { // Don't replace this typename if it's preceded by "::" unless it's a namespace if (!spaceInfo.empty() && (tok2->strAt(-2) == spaceInfo[0].className) && spaceInfo[0].isNamespace) { tok2->tokAt(-3)->deleteNext(2); simplifyType = true; } } else if (Token::Match(tok2->previous(), "case %type% :")) { tok2 = tok2->next(); } else if (duplicateTypedef(&tok2, typeName, typeDef, undefinedStruct)) { exitScope = scope; // 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 (tok2->tokAt(-2) && 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()->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; // check for derived class: class A : some_typedef { 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; // 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(); } // 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 = copyTokens(tok2, typeStart->next(), typeEnd); if (!pointers.empty()) { std::list::const_iterator iter; for (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 = copyTokens(tok2, funcStart, funcEnd); if (!inCast) tok2 = processFunc(tok2, inOperator); if (!tok2) break; tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); tok2 = 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) { tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } tok2 = copyTokens(tok2, argStart, argEnd); if (inTemplate) 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 = 0; if (functionPtrRetFuncPtr) { tok2->insertToken("("); tok2 = tok2->next(); tok4 = tok2; tok2->insertToken("*"); tok2 = tok2->next(); } // skip over variable name if there if (!inCast) { if (tok2->next()->str() != ")") tok2 = tok2->next(); } if (tok4 && functionPtrRetFuncPtr) { tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok4); } tok2 = copyTokens(tok2, argStart, argEnd); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); tok2 = 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()->str() != ")") { if (tok2->next()->str() != "(") tok2 = tok2->next(); // check for function and skip over args if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); // check for array if (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 { tok2->insertToken("("); tok2 = tok2->next(); Token *tok3 = 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(); // skip over name tok2 = tok2->next(); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } } else if (typeOf) { tok2 = copyTokens(tok2, argStart, argEnd); } else if (tok2->tokAt(2) && tok2->strAt(2) == "[") { while (tok2->tokAt(2) && tok2->strAt(2) == "[") tok2 = tok2->linkAt(2)->previous(); } if (arrayStart && arrayEnd) { do { if (!tok2->next()) { syntaxError(tok2); return; // 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); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } if (!tok2->next()) { syntaxError(tok2); return; // can't recover so quit } tok2 = copyTokens(tok2, arrayStart, arrayEnd); tok2 = tok2->next(); if (tok2->str() == "=") { if (tok2->next()->str() == "{") tok2 = tok2->next()->link()->next(); else if (tok2->next()->str().at(0) == '\"') tok2 = tok2->tokAt(2); } } while (Token::Match(tok2, ", %var% ;|'|=|,")); } simplifyType = false; } } if (tok->str() == ";") done = true; else if (tok->str() == ",") { arrayStart = 0; arrayEnd = 0; 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; 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(); } 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()) { //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 (tokbegin && (tokbegin->str() == "&" || tokbegin->str() == "(")) { 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) break; --closedpars; //remove ')' tok->deleteNext(); //remove '* ( &' tokbegin = tokbegin->tokAt(-3); tokbegin->deleteNext(3); } else break; } else if (tokbegin->str() == "(") { if (!closedpars) 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--) { tok->deleteNext(); tokbegin->deleteNext(); --closedpars; } } else break; } } } } } bool Tokenizer::tokenize(std::istream &code, const char FileName[], const std::string &configuration, bool noSymbolDB_AST) { // make sure settings specified assert(_settings); // Fill the map _typeSize.. fillTypeSizes(); _configuration = configuration; if (!list.createTokens(code, Path::getRelativePath(Path::simplifyPath(FileName), _settings->_basePaths))) { cppcheckError(0); return false; } if (simplifyTokenList1(FileName)) { if (!noSymbolDB_AST) { 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()) { const_cast(var->typeEndToken())->str("&"); const_cast(var->typeEndToken())->insertToken("&"); const_cast(var->typeEndToken()->next())->scope(var->typeEndToken()->scope()); } } list.createAst(); ValueFlow::setValues(&list, _errorLogger, _settings); } return true; } return false; } //--------------------------------------------------------------------------- bool Tokenizer::tokenizeCondition(const std::string &code) { assert(_settings); // Fill the map _typeSize.. fillTypeSizes(); { std::istringstream istr(code); if (!list.createTokens(istr, "")) { cppcheckError(0); return false; } } // Combine strings combineStrings(); // Remove "volatile", "inline", "register", and "restrict" simplifyKeyword(); // 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 _isUnsigned=true,_isLong=true simplifyStdType(); // Concatenate double sharp: 'a ## b' -> 'ab' concatenateDoubleSharp(); createLinks(); // replace 'NULL' and similar '0'-defined macros with '0' simplifyNull(); // replace 'sin(0)' to '0' and other similar math expressions simplifyMathExpressions(); // combine "- %num%" concatenateNegativeNumberAndAnyPositive(); // simplify simple calculations for (Token *tok = list.front() ? list.front()->next() : nullptr; tok; tok = tok->next()) { if (tok->isNumber()) TemplateSimplifier::simplifyNumericCalculations(tok->previous()); } combineOperators(); simplifyRedundantParentheses(); for (Token *tok = list.front(); tok; tok = tok->next()) while (TemplateSimplifier::simplifyNumericCalculations(tok)) ; simplifyCAlternativeTokens(); // Convert e.g. atol("0") into 0 while (simplifyMathFunctions()) {}; simplifyDoublePlusAndDoubleMinus(); return true; } void Tokenizer::findComplicatedSyntaxErrorsInTemplates() { const Token *tok = TemplateSimplifier::hasComplicatedSyntaxErrorsInTemplates(list.front()); if (tok) syntaxError(tok); } bool Tokenizer::hasEnumsWithTypedef() { for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "enum %var% {")) { tok = tok->tokAt(2); const Token *tok2 = Token::findsimplematch(tok, "typedef", tok->link()); if (tok2) syntaxError(tok2); } } return false; } void Tokenizer::fillTypeSizes() { _typeSize.clear(); _typeSize["char"] = 1; _typeSize["char16_t"] = 2; _typeSize["char32_t"] = 4; _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() { // 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; } // replace "->" with "." else if (c1 == '-' && c2 == '>') { tok->str("."); tok->originalName("->"); tok->deleteNext(); continue; } } else if (tok->str() == ">>" && tok->next()->str() == "=") { tok->str(">>="); tok->deleteNext(); } else if (tok->str() == "<<" && tok->next()->str() == "=") { tok->str("<<="); tok->deleteNext(); } else if ((c1 == 'p' || c1 == '_') && tok->next()->str() == ":" && tok->strAt(2) != ":") { if (tok->str() == "private" || tok->str() == "protected" || tok->str() == "public" || tok->str() == "__published") { tok->str(tok->str() + ":"); tok->deleteNext(); continue; } } } } void Tokenizer::combineStrings() { // Combine wide strings for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "L" && tok->next() && tok->next()->type() == Token::eString) { // Combine 'L "string"' 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()->type() == Token::eString) { tok->next()->str(simplifyString(tok->next()->str())); // Two strings after each other, combine them tok->concatStr(tok->next()->str()); tok->deleteNext(); } } } void Tokenizer::concatenateDoubleSharp() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "%num%|%var% ## %num%|%var%")) { tok->str(tok->str() + tok->strAt(2)); tok->deleteNext(2); } } } void Tokenizer::simplifyFileAndLineMacro() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "__FILE__") tok->str("\"" + list.file(tok) + "\""); else if (tok->str() == "__LINE__") tok->str(MathLib::toString(tok->linenr())); } } void Tokenizer::simplifyNull() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "NULL" && !Token::Match(tok->previous(), "[(,] NULL [,)]")) tok->str("0"); else if (tok->str() == "__null" || tok->str() == "'\\0'" || tok->str() == "'\\x0'") { tok->originalName(tok->str()); tok->str("0"); } else if (tok->isNumber() && MathLib::isInt(tok->str()) && MathLib::toLongNumber(tok->str()) == 0) tok->str("0"); } // nullptr.. if (isCPP() && _settings->standards.cpp == Standards::CPP11) { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "nullptr") tok->str("0"); } } } void Tokenizer::concatenateNegativeNumberAndAnyPositive() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "?|:|,|(|[|{|return|case|sizeof|%op% +|-") || tok->type() == Token::eIncDecOp) continue; if (tok->next()->str() == "+") tok->deleteNext(); else if (Token::Match(tok->next(), "- %num%")) { tok->deleteNext(); tok->next()->str("-" + tok->next()->str()); } } } void Tokenizer::simplifyExternC() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(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%|%var% ; } )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(3); } } } void Tokenizer::simplifySQL() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "EXEC SQL")) { const Token *end = tok->tokAt(2); while (end && end->str() != ";") end = end->next(); std::string instruction = tok->stringifyList(end); // delete all tokens until ';' 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::simplifyDebugNew() { // convert Microsoft DEBUG_NEW macro to new for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "DEBUG_NEW") tok->str("new"); } } void Tokenizer::simplifyArrayAccessSyntax() { // 0[a] -> a[0] for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%num% [ %var% ]")) { std::string temp = tok->str(); tok->str(tok->strAt(2)); tok->tokAt(2)->str(temp); } } } void Tokenizer::simplifyParameterVoid() { for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%var% ( 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() == "+") { tok->deleteNext(); continue; } else 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() == "+") { tok->deleteNext(); continue; } else 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() { bool addlength = false; for (Token *tok = list.front(); tok; tok = tok->next()) { 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 = tok->strAt(3).length() - 1; tok->insertToken(MathLib::toString((unsigned int)sz)); addlength = false; tok = tok->tokAt(5); } else if (Token::Match(tok, "%var% [ ] = {")) { unsigned int sz = 1; tok = tok->next(); Token *end = tok->linkAt(3); for (Token *tok2 = tok->tokAt(4); tok2 && tok2 != end; tok2 = tok2->next()) { if (tok2->str() == "{" || tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (tok2->str() == "<") { // Bailout. TODO: When link() supports <>, this bailout becomes unnecessary sz = 0; break; } 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) { if (!tok || tok->str() != "?") return 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 (Token::Match(tok->next(), "[{};]")) break; } if (colonlevel) // Ticket #5214: Make sure the ':' matches the proper '?' return 0; return tok; } Token * Tokenizer::startOfFunction(Token * tok) { if (tok && tok->str() == ")") { tok = tok->next(); while (tok && tok->str() != "{") { if (tok->str() == "const" || tok->str() == "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 nullptr; } return tok; } return nullptr; } const Token * Tokenizer::startOfExecutableScope(const Token * tok) { if (tok && tok->str() == ")") { tok = tok->next(); bool inInit = false; while (tok && tok->str() != "{") { if (!inInit) { if (tok->str() == "const" || tok->str() == "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(); } else if (tok->str() == ":") { inInit = true; tok = tok->next(); } // unknown macros ") MACRO {" and ") MACRO(...) {" else if (tok->isUpperCaseName()) { tok = tok->next(); if (tok && tok->str() == "(") { tok = tok->link()->next(); } } else return nullptr; } else { if (tok->isName() && tok->next() && tok->next()->str() == "(") { tok = tok->next()->link()->next(); } else if (tok->str() == ",") { tok = tok->next(); } else return nullptr; } } return tok; } return nullptr; } /** simplify labels and case|default in the code: add a ";" if not already in.*/ void Tokenizer::simplifyLabelsCaseDefault() { bool executablescope = false; unsigned int indentlevel = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { // Simplify labels in the executable scope.. Token *start = 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) { executablescope = false; continue; } } else if (tok->str() == "(" || tok->str() == "[") tok = tok->link(); if (Token::Match(tok, "[;{}] case")) { while (nullptr != (tok = tok->next())) { if (tok->str() == "(" || tok->str() == "[") { 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->str() != "case" && tok->next() && tok->next()->str() == ":") { tok = tok->next(); if (tok->next()->str() != ";") tok->insertToken(";"); } else { syntaxError(tok); } } else if (Token::Match(tok, "[;{}] %var% : !!;")) { tok = tok->tokAt(2); tok->insertToken(";"); } } } void Tokenizer::simplifyTemplates() { if (isC()) return; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str()[0] == '=' && tok->str().length() > 2) { std::cout << "BAD TOKEN" << std::endl; } } for (Token *tok = list.front(); tok; tok = tok->next()) { // #2648 - simple fix for sizeof used as template parameter // TODO: this is a bit hardcoded. make a bit more generic if (Token::Match(tok, "%var% < 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)); } } TemplateSimplifier::simplifyTemplates( list, _errorLogger, _settings, _codeWithTemplates); } //--------------------------------------------------------------------------- static bool setVarIdParseDeclaration(const Token **tok, const std::map &variableId, bool executableScope, bool cpp) { const Token *tok2 = *tok; bool ref = false; 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; while (tok2) { if (tok2->isName()) { if (tok2->str() == "struct" || tok2->str() == "union" || (cpp && (tok2->str() == "class" || tok2->str() == "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 { ++typeCount; ++singleNameCount; } } else if ((TemplateSimplifier::templateParameters(tok2) > 0) || Token::simpleMatch(tok2, "< >") /* Ticket #4764 */) { tok2 = tok2->findClosingBracket(); if (!Token::Match(tok2, ">|>>")) break; singleNameCount = 1; } else if (tok2->str() == "&" || tok2->str() == "&&") { ref = !bracket; } else if (singleNameCount == 1 && tok2->str() == "(" && 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 (tok2->str() == "(" || tok2->str() == "=") ; // 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 && tok2 && tok2->str() == "[" && executableScope) { const Token *tok3 = tok2; while (tok3 && tok3->str() == "[") { tok3 = tok3->link()->next(); } if (Token::Match(tok3, "= %num%")) return false; } return bool(typeCount >= 2 && tok2 && Token::Match(tok2->tokAt(-2), "!!:: %type%")); } static void setVarIdStructMembers(Token **tok1, std::map > *structMembers, unsigned int *_varId) { Token *tok = *tok1; while (Token::Match(tok->next(), ". %var% !!(")) { 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]; if (members.empty() || members.find(tok->str()) == members.end()) { members[tok->str()] = ++(*_varId); tok->varId(*_varId); } else { tok->varId(members[tok->str()]); } } if (tok) *tok1 = tok; } static void setVarIdClassDeclaration(Token * const startToken, const std::map &variableId, const unsigned int scopeStartVarId, std::map > *structMembers, unsigned int *_varId) { // 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 %type% [:{]")) { className = tok->next()->str(); break; } } // replace varids.. unsigned int indentlevel = 0; bool initList = false; for (Token *tok = startToken->next(); tok != endToken; tok = tok->next()) { if (tok->str() == "{") { initList = false; ++indentlevel; } else if (tok->str() == "}") --indentlevel; else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %var% (")) { 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) { if (Token::Match(tok->previous(), "::|.")) continue; if (tok->next()->str() == "::") { if (tok->str() == className) tok = tok->tokAt(2); else continue; } 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() == ":") 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), "%var% :: %var% ::")) // Currently unsupported continue; if (Token::Match(tok2->tokAt(-2), "!!this . ")) continue; const std::map::const_iterator it = varlist.find(tok2->str()); if (it != varlist.end()) { tok2->varId(it->second); setVarIdStructMembers(&tok2, structMembers, _varId); } } } static bool isInitList(const Token *tok) { if (!Token::Match(tok, ") : %var% (")) return false; tok = tok->linkAt(3); while (Token::Match(tok, ") , %var% (")) tok = tok->linkAt(3); return Token::simpleMatch(tok, ") {"); } void Tokenizer::setVarId() { // Clear all variable ids for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isName()) tok->varId(0); } setPodTypes(); // Variable declarations can't start with "return" etc. std::set notstart; notstart.insert("goto"); notstart.insert("NOT"); notstart.insert("return"); notstart.insert("sizeof"); if (!isC()) { static const char *str[] = {"delete","friend","new","throw","using","virtual","explicit"}; notstart.insert(str, str+(sizeof(str)/sizeof(*str))); } // variable id _varId = 0; std::map variableId; std::map > structMembers; std::stack< std::map > scopeInfo; std::stack executableScope; executableScope.push(false); std::stack scopestartvarid; // varid when scope starts scopestartvarid.push(0); bool initlist = false; for (Token *tok = list.front(); tok; tok = tok->next()) { // scope info to handle shadow variables.. if (!initlist && tok->str() == "(" && (Token::simpleMatch(tok->link(), ") {") || Token::Match(tok->link(), ") %type% {") || isInitList(tok->link()))) { scopeInfo.push(variableId); initlist = Token::simpleMatch(tok->link(), ") :"); // function declarations } else if (!executableScope.top() && tok->str() == "(" && Token::Match(tok->link(), ") const| ;")) { scopeInfo.push(variableId); } else if (!executableScope.top() && Token::Match(tok, ") const| ;")) { variableId.swap(scopeInfo.top()); scopeInfo.pop(); } else if (tok->str() == "{") { initlist = false; // parse anonymous unions as part of the current scope if (!(tok->strAt(-1) == "union" && Token::simpleMatch(tok->link(), "} ;"))) { scopestartvarid.push(_varId); if (tok->strAt(-1) == ")" || Token::Match(tok->tokAt(-2), ") %type%")) { executableScope.push(true); } else { executableScope.push(tok->strAt(-1) == "else"); scopeInfo.push(variableId); } } } else if (tok->str() == "}") { // parse anonymous unions as part of the current scope if (!(Token::simpleMatch(tok, "} ;") && Token::simpleMatch(tok->link()->previous(), "union {"))) { // Set variable ids in class declaration.. if (!isC() && !executableScope.top() && tok->link()) { setVarIdClassDeclaration(tok->link(), variableId, scopestartvarid.top(), &structMembers, &_varId); } scopestartvarid.pop(); if (scopestartvarid.empty()) { // should be impossible scopestartvarid.push(0); } if (scopeInfo.empty()) { variableId.clear(); } else { variableId.swap(scopeInfo.top()); scopeInfo.pop(); } executableScope.pop(); if (executableScope.empty()) { // should not possibly happen executableScope.push(false); } } } if (tok == list.front() || Token::Match(tok, "[;{}]") || (Token::Match(tok,"[(,]") && (!executableScope.top() || Token::simpleMatch(tok->link(), ") {"))) || (tok->isName() && tok->str().at(tok->str().length()-1U) == ':')) { // 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 && tok2->str()[tok2->str().size() - 1U] == ':') { tok2 = tok2->next(); } if (!tok2) break; // Variable declaration can't start with "return", etc if (notstart.find(tok2->str()) != notstart.end()) continue; const bool decl = setVarIdParseDeclaration(&tok2, variableId, executableScope.top(), isCPP()); if (decl && Token::Match(tok2->previous(), "%type% [;[=,)]") && tok2->previous()->str() != "const") { variableId[tok2->previous()->str()] = ++_varId; tok = tok2->previous(); } else if (decl && Token::Match(tok2->previous(), "%type% ( !!)") && Token::simpleMatch(tok2->link(), ") ;")) { // In C++ , a variable can't be called operator+ or something like that. if (isCPP() && tok2->previous()->str().size() >= 9 && tok2->previous()->str().compare(0, 8, "operator") == 0 && tok2->previous()->str()[8] != '_' && !std::isalnum(tok2->previous()->str()[8])) continue; const Token *tok3 = tok2->next(); if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && (notstart.find(tok3->str()) != notstart.end() ||!setVarIdParseDeclaration(&tok3, variableId, executableScope.top(), isCPP()))) { variableId[tok2->previous()->str()] = ++_varId; tok = tok2->previous(); } } } if (tok->isName()) { // don't set variable id after a struct|enum|union if (Token::Match(tok->previous(), "struct|enum|union")) continue; if (!isC()) { if (tok->previous() && tok->previous()->str() == "::") continue; if (tok->next() && tok->next()->str() == "::") continue; } 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, "::|. %var%")) { // 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(); } } // Clear the structMembers because it will be used when member functions // are parsed. The old info is not bad, it is just redundant. structMembers.clear(); // Member functions and variables in this source std::list allMemberFunctions; std::list allMemberVars; { for (Token *tok2 = list.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "%var% :: %var%")) { if (tok2->strAt(3) == "(") allMemberFunctions.push_back(tok2); else if (tok2->tokAt(2)->varId() != 0) allMemberVars.push_back(tok2); } } } // class members.. for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "class|struct %var% {|:")) { const std::string &classname(tok->next()->str()); // What member variables are there in this class? std::map varlist; const Token* tokStart = Token::findsimplematch(tok, "{"); if (tokStart) { 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(classname, tok2, tok2->link(), varlist, &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) varlist[tok2->str()] = tok2->varId(); } } // Are there any member variables in this class? if (varlist.empty()) continue; // Member variables for (std::list::iterator func = allMemberVars.begin(); func != allMemberVars.end(); ++func) { if (!Token::simpleMatch(*func, classname.c_str())) continue; Token *tok2 = *func; tok2 = tok2->tokAt(2); tok2->varId(varlist[tok2->str()]); } // Set variable ids in member functions for this class.. const std::string funcpattern(classname + " :: %var% ("); for (std::list::iterator func = allMemberFunctions.begin(); func != allMemberFunctions.end(); ++func) { Token *tok2 = *func; // Found a class function.. if (Token::Match(tok2, funcpattern.c_str())) { // Goto the end parentheses.. tok2 = tok2->linkAt(3); if (!tok2) break; // If this is a function implementation.. add it to funclist Token * start = startOfFunction(tok2); if (start) { setVarIdClassFunction(classname, start, start->link(), varlist, &structMembers, &_varId); } // constructor with initializer list if (Token::Match(tok2, ") : %var% (")) { Token *tok3 = tok2; while (Token::Match(tok3, ") [:,] %var% (")) { Token *vartok = tok3->tokAt(2); if (varlist.find(vartok->str()) != varlist.end()) vartok->varId(varlist[vartok->str()]); tok3 = tok3->linkAt(3); } if (Token::simpleMatch(tok3, ") {")) { setVarIdClassFunction(classname, tok2, tok3->next()->link(), varlist, &structMembers, &_varId); } } } } } } } static void linkBrackets(Tokenizer* tokenizer, std::stack& type, std::stack& links, Token* token, char open, 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(0); } 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; std::stack type; for (Token *token = list.front(); token; token = token->next()) { 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(); } else token->link(0); } else if (token->str() == ";") while (!type.empty() && type.top()->str() == "<") type.pop(); else if (token->str() == "<" && token->previous() && token->previous()->isName() && !token->previous()->varId()) type.push(token); else if (token->str() == ">" || token->str() == ">>") { if (type.empty() || type.top()->str() != "<") // < and > don't match. continue; if (token->next() && !Token::Match(token->next(), "%var%|>|>>|&|*|::|,|(|)|{|;|[")) continue; // Check type of open link if (token->str() == ">>" && type.size() < 2) continue; // if > is followed by ; .. "new a;" is expected // if > is followed by [ .. "new a[" is expected if (Token::Match(token->next(), ";|[")) { Token *prev = type.top()->previous(); while (prev && Token::Match(prev->previous(), ":: %var%")) prev = prev->tokAt(-2); if (prev && prev->str() != "new") prev = prev->previous(); if (!prev || prev->str() != "new") continue; } Token* top = type.top(); type.pop(); if (token->str() == ">>" && type.top()->str() != "<") { type.push(top); continue; } if (token->str() == ">>") { // C++11 right angle bracket token->str(">"); token->insertToken(">"); } Token::createMutualLinks(top, token); } } } void Tokenizer::sizeofAddParentheses() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof %var%")) { Token *tempToken = tok->next(); while (Token::Match(tempToken, "%var%")) { while (tempToken && tempToken->next() && tempToken->next()->str() == "[") tempToken = tempToken->next()->link(); if (!tempToken || !tempToken->next()) break; if (Token::Match(tempToken->next(), ". %var%")) { // We are checking a class or struct, search next varname tempToken = tempToken->tokAt(2); continue; } else if (tempToken->next()->type() == Token::eIncDecOp) { // We have variable++ or variable--, the sizeof argument // ends after the op tempToken = tempToken->next(); } else if (Token::Match(tempToken->next(), "[),;}]")) { ; } else { break; } // Ok. Add ( after sizeof and ) after tempToken tok->insertToken("("); tempToken->insertToken(")"); Token::createMutualLinks(tok->next(), tempToken->next()); break; } } } } bool Tokenizer::simplifySizeof() { // Locate variable declarations and calculate the size std::map sizeOfVar; for (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% * %var% [;,)]") || Token::Match(tok->tokAt(-4), "[;{}(,] const %type% * %var% [;),]") || Token::Match(tok->tokAt(-2), "[;{}(,] %type% %var% [;),]") || Token::Match(tok->tokAt(-3), "[;{}(,] const %type% %var% [;),]")) { const unsigned int size = sizeOfType(tok->previous()); if (size == 0) { continue; } sizeOfVar[varId] = size; } else if (Token::Match(tok->previous(), "%type% %var% [ %num% ] [;=]") || Token::Match(tok->tokAt(-2), "%type% * %var% [ %num% ] [;=]")) { const unsigned int size = sizeOfType(tok->previous()); if (size == 0) continue; sizeOfVar[varId] = size * static_cast(MathLib::toLongNumber(tok->strAt(2))); } else if (Token::Match(tok->previous(), "%type% %var% [ %num% ] [,)]") || Token::Match(tok->tokAt(-2), "%type% * %var% [ %num% ] [,)]")) { Token tempTok(0); tempTok.str("*"); sizeOfVar[varId] = sizeOfType(&tempTok); } } } bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "sizeof") continue; if (!tok->next()) break; if (tok->strAt(1) == "sizeof") continue; if (Token::simpleMatch(tok->next(), ". . .")) { tok->deleteNext(3); } // sizeof 'x' if (tok->next()->type() == Token::eChar) { tok->deleteThis(); std::ostringstream sz; sz << sizeof 'x'; tok->str(sz.str()); ret = true; continue; } // sizeof('x') if (Token::Match(tok, "sizeof ( %char% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream sz; sz << sizeof 'x'; tok->str(sz.str()); ret = true; continue; } // sizeof "text" if (tok->next()->type() == Token::eString) { tok->deleteThis(); std::ostringstream ostr; ostr << (Token::getStrLength(tok) + 1); tok->str(ostr.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 * (...) -> sizeof(*...) if (Token::simpleMatch(tok->next(), "* (") && !Token::simpleMatch(tok->linkAt(2), ") .")) { tok->deleteNext(); tok->next()->insertToken("*"); } // sizeof a++ -> sizeof(a++) if (Token::Match(tok->next(), "++|-- %var% !!.") || Token::Match(tok->next(), "%var% ++|--")) { tok->insertToken("("); tok->tokAt(3)->insertToken(")"); Token::createMutualLinks(tok->next(), tok->tokAt(4)); } // sizeof 1 => sizeof ( 1 ) if (tok->next()->isNumber()) { Token *tok2 = tok->next(); tok->insertToken("("); tok2->insertToken(")"); Token::createMutualLinks(tok->next(), tok2->next()); } // sizeof int -> sizeof( int ) else if (tok->next()->str() != "(") { // Add parentheses around the sizeof int parlevel = 0; for (Token *tempToken = tok->next(); tempToken; tempToken = tempToken->next()) { if (tempToken->str() == "(") ++parlevel; else if (tempToken->str() == ")") { --parlevel; if (parlevel == 0 && !Token::Match(tempToken, ") . %var%")) { // Ok, we should be clean. Add ) after tempToken tok->insertToken("("); tempToken->insertToken(")"); Token::createMutualLinks(tok->next(), tempToken->next()); break; } } if (parlevel == 0 && Token::Match(tempToken, "%var%")) { while (tempToken && tempToken->next() && tempToken->next()->str() == "[") { tempToken = tempToken->next()->link(); } if (!tempToken || !tempToken->next()) { break; } if (tempToken->next()->str() == ".") { // We are checking a class or struct, search next varname tempToken = tempToken->next(); continue; } else if (tempToken->next()->type() == Token::eIncDecOp) { // We have variable++ or variable--, there should be // nothing after this tempToken = tempToken->tokAt(2); } // Ok, we should be clean. Add ) after tempToken tok->insertToken("("); tempToken->insertToken(")"); Token::createMutualLinks(tok->next(), tempToken->next()); break; } } } // 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% )") && tok->tokAt(2)->varId() != 0) { if (sizeOfVar.find(tok->tokAt(2)->varId()) != sizeOfVar.end()) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); tok->str(MathLib::toString(sizeOfVar[tok->varId()])); ret = true; } else { // don't try to replace size of variable if variable has // similar name with type (#329) } } else if (Token::Match(tok, "sizeof ( %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::Match(tok, "sizeof ( * %var% )") || Token::Match(tok, "sizeof ( %var% [ %num% ] )")) { // Some default value.. std::size_t sz = 0; const unsigned int varid = tok->tokAt((tok->strAt(2) == "*") ? 3 : 2)->varId(); if (varid != 0) { // Try to locate variable declaration.. const Token *decltok = Token::findmatch(list.front(), "%varid%", varid); if (Token::Match(decltok->previous(), "%type% %var% [")) { sz = sizeOfType(decltok->previous()); } else if (Token::Match(decltok->previous(), "* %var% [")) { sz = sizeOfType(decltok->previous()); } else if (Token::Match(decltok->tokAt(-2), "%type% * %var%")) { sz = sizeOfType(decltok->tokAt(-2)); } } else if (tok->strAt(3) == "[" && tok->tokAt(2)->isStandardType()) { sz = sizeOfType(tok->tokAt(2)); if (sz == 0) continue; sz *= static_cast(MathLib::toLongNumber(tok->strAt(4))); } if (sz > 0) { tok->str(MathLib::toString(sz)); 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 %var% (")) { if (Token::simpleMatch(tok, "for each")) { // 'for each ( )' -> 'asm ( )' tok->str("asm"); tok->deleteNext(); } else { syntaxError(tok); return false; } } } // remove MACRO in variable declaration: MACRO int x; removeMacroInVarDecl(); // Combine strings combineStrings(); // replace inline SQL with "asm()" (Oracle PRO*C). Ticket: #1959 simplifySQL(); // replace __LINE__ macro with line number simplifyFileAndLineMacro(); // Concatenate double sharp: 'a ## b' -> 'ab' concatenateDoubleSharp(); createLinks(); // 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, ") %var% (") && tok->next()->isUpperCaseName() && Token::Match(tok->linkAt(2), ") {|else")) { syntaxError(tok->next()); return false; } } } if (_settings->terminated()) return false; // replace 'NULL' and similar '0'-defined macros with '0' simplifyNull(); // replace 'sin(0)' to '0' and other similar math expressions simplifyMathExpressions(); // combine "- %num%" concatenateNegativeNumberAndAnyPositive(); // simplify simple calculations for (Token *tok = list.front() ? list.front()->next() : nullptr; tok; tok = tok->next()) { if (tok->isNumber()) TemplateSimplifier::simplifyNumericCalculations(tok->previous()); } // 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); list.deallocateTokens(); return false; } } if (!simplifyAddBraces()) return false; sizeofAddParentheses(); // Combine tokens.. combineOperators(); // 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(); // Convert K&R function declarations to modern C simplifyVarDecl(true); simplifyFunctionParameters(); // specify array size.. arraySize(); // simplify labels and 'case|default'-like syntaxes simplifyLabelsCaseDefault(); // simplify '[;{}] * & ( %any% ) =' to '%any% =' simplifyMulAndParens(); // ";a+=b;" => ";a=a+b;" simplifyCompoundAssignment(); if (!_settings->library.markupFile(FileName)) { findComplicatedSyntaxErrorsInTemplates(); } if (_settings->terminated()) return false; // remove calling conventions __cdecl, __stdcall.. simplifyCallingConvention(); // Remove __declspec() simplifyDeclspec(); // remove some unhandled macros in global scope removeMacrosInGlobalScope(); // remove __attribute__((?)) simplifyAttribute(); // remove unnecessary member qualification.. removeUnnecessaryQualification(); // remove Microsoft MFC.. simplifyMicrosoftMFC(); // 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(); // Remove __builtin_expect, likely and unlikely simplifyBuiltinExpect(); if (hasEnumsWithTypedef()) { // #2449: syntax error: enum with typedef in it list.deallocateTokens(); return false; } simplifyDebugNew(); // typedef.. if (m_timerResults) { Timer t("Tokenizer::tokenize::simplifyTypedef", _settings->_showtime, m_timerResults); simplifyTypedef(); } else { simplifyTypedef(); } 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 (_settings->isEnabled("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(); // enum.. simplifyEnum(); // The simplify enum have inner loops if (_settings->terminated()) return false; // Remove __asm.. simplifyAsm(); // Put ^{} statements in asm() for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "^ {")) { Token * start = tok; while (start && !Token::Match(start, "[;{}]")) start = start->previous(); if (start) start = start->next(); const Token *last = tok->next()->link(); if (start != tok) { last = last->next(); while (last && !Token::Match(last->next(), "[;{}()]")) last = last->next(); } if (start && last) { std::string asmcode(start->str()); while (start->next() != last) { asmcode += start->next()->str(); start->deleteNext(); } asmcode += last->str(); start->deleteNext(); start->insertToken(";"); start->insertToken(")"); start->insertToken("\"" + asmcode + "\""); start->insertToken("("); start->str("asm"); start->link(nullptr); start->next()->link(start->tokAt(3)); start->tokAt(3)->link(start->next()); 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(); else if (tok->str()[0] == '@') { list.deallocateTokens(); return false; } } // 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 _isUnsigned=true,_isLong=true simplifyStdType(); // The simplifyTemplates have inner loops if (_settings->terminated()) return false; // simplify bit fields.. simplifyBitfields(); // Simplify '(p == 0)' to '(!p)' simplifyIfNot(); simplifyIfNotNull(); // The simplifyTemplates have inner loops if (_settings->terminated()) return false; simplifyConst(); // struct simplification "struct S {} s; => struct S { } ; S s ; simplifyStructDecl(); // struct initialization (must be used before simplifyVarDecl) simplifyStructInit(); // Change initialisation of variable to assignment simplifyInitVar(); // The simplifyTemplates have inner loops if (_settings->terminated()) return false; // Split up variable declarations. simplifyVarDecl(false); // specify array size.. needed when arrays are split arraySize(); // f(x=g()) => x=g(); f(x) simplifyAssignmentInFunctionCall(); // x = ({ 123; }); => { x = 123; } simplifyAssignmentBlock(); // The simplifyTemplates have inner loops if (_settings->terminated()) return false; simplifyVariableMultipleAssign(); // Simplify float casts (float)1 => 1.0 simplifyFloatCasts(); // Remove redundant parentheses simplifyRedundantParentheses(); 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; // Simplify templates.. 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. TemplateSimplifier::cleanupAfterSimplify(list.front()); // Collapse operator name tokens into single token // operator = => operator= simplifyOperatorName(); // 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); if (m_timerResults) { Timer t("Tokenizer::tokenize::setVarId", _settings->_showtime, m_timerResults); setVarId(); } else { setVarId(); } // Link < with > createLinks2(); // Simplify the C alternative tokens (and, or, etc.) simplifyCAlternativeTokens(); // 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 while (simplifyMathFunctions()) {}; simplifyDoublePlusAndDoubleMinus(); simplifyArrayAccessSyntax(); Token::assignProgressValues(list.front()); removeRedundantSemicolons(); simplifyParameterVoid(); simplifyRedundantConsecutiveBraces(); simplifyEmptyNamespaces(); elseif(); // 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("&"); } } validate(); return true; } bool Tokenizer::simplifyTokenList2() { // clear the _functionList so it can't contain dead pointers deleteSymbolDatabase(); // Experimental AST handling. for (Token *tok = list.front(); tok; tok = tok->next()) tok->clearAst(); 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(); // Replace constants.. for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "const static| %type% %var% = %num% ;") || Token::Match(tok, "const static| %type% %var% ( %num% ) ;")) { unsigned int offset = 0; if (tok->strAt(1) == "static") offset = 1; const unsigned int varId(tok->tokAt(2 + offset)->varId()); if (varId == 0) { tok = tok->tokAt(5 + offset); continue; } const std::string& num = tok->strAt(4 + offset); int indent = 1; for (Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { ++indent; } else if (tok2->str() == "}") { --indent; if (indent == 0) break; } // Compare constants, but don't touch members of other structures else if (tok2->varId() == varId) { tok2->str(num); } } } } if (_settings->terminated()) return false; // Simplify simple calculations.. simplifyCalculations(); // Replace "*(ptr + num)" => "ptr[num]" simplifyOffsetPointerDereference(); // Replace "&str[num]" => "(str + num)" 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%|%var%|]|)") && (Token::Match(tok->next(), "& %var% [ %num%|%var% ] !!["))) { tok = tok->next(); if (tok->next()->varId()) { if (pod.find(tok->next()->varId()) == pod.end()) { tok = tok->tokAt(5); continue; } } // '&' => '(' tok->str("("); tok = tok->next(); // '[' => '+' tok->deleteNext(); tok->insertToken("+"); tok = tok->tokAt(3); //remove ']' tok->str(")"); Token::createMutualLinks(tok->tokAt(-4), tok); } } removeRedundantAssignment(); simplifyRealloc(); // Change initialisation of variable to assignment simplifyInitVar(); // Simplify variable declarations simplifyVarDecl(false); simplifyErrNoInWhile(); simplifyIfAndWhileAssign(); simplifyRedundantParentheses(); simplifyIfNot(); simplifyIfNotNull(); simplifyIfSameInnerCondition(); simplifyNestedStrcat(); simplifyWhile0(); simplifyFuncInWhile(); simplifyIfAndWhileAssign(); // Could be affected by simplifyIfNot bool modified = true; while (modified) { if (_settings->terminated()) return false; modified = false; modified |= simplifyConditions(); modified |= simplifyFunctionReturn(); modified |= simplifyKnownVariables(); // 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); modified = true; } } modified |= removeRedundantConditions(); modified |= simplifyRedundantParentheses(); modified |= simplifyConstTernaryOp(); modified |= simplifyCalculations(); } // simplify redundant for 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(); simplifyStaticConst(); while (simplifyMathFunctions()) {}; validate(); Token::assignProgressValues(list.front()); // Create symbol database and then remove const keywords createSymbolDatabase(); for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "* const")) tok->deleteNext(); } list.createAst(); ValueFlow::setValues(&list, _errorLogger, _settings); if (_settings->terminated()) return false; printDebugOutput(); return true; } //--------------------------------------------------------------------------- void Tokenizer::printDebugOutput() const { if (_settings->debug) { list.front()->printOut(0, 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"); } list.front()->printAst(_settings->_verbose, _settings->_xml, std::cout); list.front()->printValueFlow(_settings->_xml, std::cout); if (_settings->_xml) std::cout << "" << std::endl; } if (_settings->debugwarnings) { printUnknownTypes(); // #5054 - the typeStartToken() should come before typeEndToken() for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->varId() == 0U) continue; const Variable *var = tok->variable(); if (!var) continue; const Token * typetok = var->typeStartToken(); while (typetok && typetok != var->typeEndToken()) typetok = typetok->next(); if (typetok != var->typeEndToken()) { reportError(tok, Severity::debug, "debug", "Variable::typeStartToken() is not located before Variable::typeEndToken(). The location of the typeStartToken() is '" + var->typeStartToken()->str() + "' at line " + MathLib::toString(var->typeStartToken()->linenr())); } } } } static std::string 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(); } 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=\"" << toxml(tok->str()) << '\"'; out << " scope=\"" << tok->scope() << '\"'; if (tok->isName()) out << " type=\"name\""; 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->type() == Token::eString) out << " type=\"string\" strlen=\"" << Token::getStrLength(tok) << '\"'; else if (tok->type() == 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->type() == 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->astParent()) out << " astParent=\"" << tok->astParent() << '\"'; if (tok->astOperand1()) out << " astOperand1=\"" << tok->astOperand1() << '\"'; if (tok->astOperand2()) out << " astOperand2=\"" << tok->astOperand2() << '\"'; out << "/>" << std::endl; } out << " " << std::endl; _symbolDatabase->printXml(out); 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% (") && Token::Match(tok2->next()->link(), ") const| {")) { 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; } } if (tok->str() == "{") tok = tok->link(); } } //--------------------------------------------------------------------------- void Tokenizer::removeMacroInVarDecl() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %var% (") && 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 = 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 '{' 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% * %var% ;") && tok2->next()->str() != "return") { tok2 = tok2->tokAt(3); localvars.insert(tok2->varId()); } else if (Token::Match(tok2, "[;{}] %type% %var% ;") && tok2->next()->isStandardType()) { tok2 = tok2->tokAt(2); localvars.insert(tok2->varId()); } else if (tok2->varId() && !Token::Match(tok2->previous(), "[;{}] %var% = %char%|%num%|%var% ;")) { localvars.erase(tok2->varId()); } } localvars.erase(0); if (!localvars.empty()) { for (Token *tok2 = tok->next(); tok2 && tok2 != end;) { if (Token::Match(tok2, "[;{}] %type% %var% ;") && localvars.find(tok2->tokAt(2)->varId()) != localvars.end()) { tok2->deleteNext(3); } else if ((Token::Match(tok2, "[;{}] %type% * %var% ;") && localvars.find(tok2->tokAt(3)->varId()) != localvars.end()) || (Token::Match(tok2, "[;{}] %var% = %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 (tok->str() == "(" || tok->str() == "[" || (tok->str() == "{" && tok->previous() && tok->previous()->str() == "=")) tok = tok->link(); else if (Token::Match(tok, "[;{}] %var% = 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 (tok->str() == "(" || tok->str() == "[" || tok->str() == "{") { tok = tok->link(); continue; } if (!Token::Match(tok, "namespace %var% {")) continue; if (tok->strAt(3) == "}") { tok->deleteNext(3); // remove '%var% { }' 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 (begin->str() == "(" || begin->str() == "[" || (begin->str() == "{" && begin->previous() && begin->strAt(-1) == "=")) begin = begin->link(); //function scope if (!Token::simpleMatch(begin, ") {") && !Token::Match(begin, ") %var% {")) 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 (tok->str() == "(" || tok->str() == "[") { tok = tok->link(); continue; } if (tok->str() == "{") { if (tok->previous() && tok->previous()->str() == "=") { tok = tok->link(); continue; } ++indentlevel; } else if (tok->str() == "}") { if (!indentlevel) break; --indentlevel; if (stilldead) { eraseDeadCode(tok, 0); if (indentlevel == 1 || tok->next()->str() != "}" || !Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) stilldead = false; continue; } } if (!indentlevel) continue; if (Token::Match(tok,"continue|break ;")) { tok = tok->next(); eraseDeadCode(tok, 0); } else if (Token::Match(tok,"return|goto") || (Token::Match(tok->previous(), "[;{}] %var% (") && _settings->library.isnoreturn(tok->str())) || (tok->str() == "throw" && !isC())) { //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 (tok2->str() == "(" || tok2->str() == "[") { tok2 = tok2->link(); } else if (tok2->str() == ";") { tok = tok2; eraseDeadCode(tok, 0); break; } else if (Token::Match(tok2, "[{}]")) break; //Wrong code. } //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 (tok->str() != "if") continue; if (!Token::Match(tok->next(), "( %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 == false) { // 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 == false) { //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 ( %var% = %num% ; %var% < %num% ; ++| %var% ++| ) {")) { // Same variable name.. const std::string varname(tok->strAt(3)); const unsigned int varid(tok->tokAt(3)->varId()); if (varname != tok->strAt(7)) continue; const Token *vartok = tok->tokAt(11); if (vartok->str() == "++") vartok = vartok->next(); if (varname != vartok->str()) continue; // Check that the difference of the numeric values is 1 const MathLib::bigint num1(MathLib::toLongNumber(tok->strAt(5))); const MathLib::bigint num2(MathLib::toLongNumber(tok->strAt(9))); 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) { // remove "for (" tok->deleteNext(2); // If loop variable is read then keep assignment before // loop body.. if (read) { // goto ";" tok = tok->tokAt(4); } else { // remove "x = 0 ;" tok->deleteNext(4); } // remove "x < 1 ; x ++ )" tok->deleteNext(7); // 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->str() == "(") { tok = tok->link(); } 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 (tokPossibleDo && tokPossibleDo->str()=="}") tokPossibleDo=tokPossibleDo->link(); if (tokPossibleDo) tokPossibleDo=tokPossibleDo->previous(); if (!tokPossibleDo || tokPossibleDo->str()!="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) { // no while return input token syntaxError(tok); return nullptr; } } } } else if (tok->str()=="if") { tokEnd=simplifyAddBracesPair(tok,true); if (!tokEnd) return nullptr; Token * tokEndNext=tokEnd->next(); if (tokEndNext && tokEndNext->str()=="else") { Token * tokEndNextNext=tokEndNext->next(); if (tokEndNextNext && tokEndNextNext->str()=="if") { // do not change "else if ..." to "else { if ... }" tokEnd=simplifyAddBracesToCommand(tokEndNextNext); } else if (Token::simpleMatch(tokEndNext, "else }")) { syntaxError(tokEndNext); return nullptr; } else tokEnd=simplifyAddBracesPair(tokEndNext,false); } } return tokEnd; } Token *Tokenizer::simplifyAddBracesPair(Token *tok, bool commandWithCondition) { Token * tokCondition=tok->next(); Token *tokAfterCondition=tokCondition; if (commandWithCondition) { if (!tokCondition) { // Missing condition return tok; } if (tokCondition->str()=="(") tokAfterCondition=tokCondition->link(); else tokAfterCondition=nullptr; if (!tokAfterCondition) { // Bad condition syntaxError(tok); return nullptr; } tokAfterCondition=tokAfterCondition->next(); } if (!tokAfterCondition || ((tokAfterCondition->type()==Token::eBracket || tokAfterCondition->type()==Token::eExtendedOp)&& 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); tokEnd->insertToken("}"); Token * tokCloseBrace = tokEnd->next(); Token::createMutualLinks(tokOpenBrace, tokCloseBrace); tokBracesEnd = tokCloseBrace; } 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 && tokEnd->str()!=";" && !((tokEnd->type()==Token::eBracket || tokEnd->type()==Token::eExtendedOp)&& Token::Match(tokEnd,")|}|>"))) { if (tokEnd->type()==Token::eBracket || (tokEnd->type()==Token::eExtendedOp && tokEnd->str()=="(")) { Token *tokInnerCloseBraket=tokEnd->link(); if (!tokInnerCloseBraket) { // Inner bracket does not close return tok; } tokEnd=tokInnerCloseBraket; } 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, "[;{}] (| *| (| %var%")) { // 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, ". %var%") || Token::Match(tok, "[|(")) { if (tok->str() == ".") tok = tok->tokAt(2); else { // goto "]" or ")" tok = tok->link(); // goto next token.. tok = tok ? tok->next() : 0; } } } 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->str() == "(") 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->type() == 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 (tok->str() == "0" || tok->str() == "false") tok->str("true"); else tok->str("false"); ret = true; } if (Token::simpleMatch(tok, "( true &&") || Token::simpleMatch(tok, "&& true &&") || Token::simpleMatch(tok->next(), "&& true )")) { tok->deleteNext(2); ret = true; } else if (Token::simpleMatch(tok, "( false ||") || Token::simpleMatch(tok, "|| false ||") || Token::simpleMatch(tok->next(), "|| false )")) { 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); if (cmp == "==") result = eq; else result = !eq; } else { double op1 = MathLib::toDoubleNumber(tok->next()->str()); 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 = ""; } } else { // Compare boolean bool op1 = (tok->next()->str() == std::string("true")); 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 = ""; } if (! cmp.empty()) { tok = tok->next(); tok->deleteNext(2); tok->str(result ? "true" : "false"); ret = true; } } } return ret; } bool Tokenizer::simplifyConstTernaryOp() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { 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) == "<" && !TemplateSimplifier::templateParameters(tok->tokAt(-2*offset))) continue; // Find the token ":" then go to the next token Token *semicolon = skipTernaryOp(tok); if (!semicolon || semicolon->previous()->str() != ":" || !semicolon->next()) continue; //handle the GNU extension: "x ? : y" <-> "x ? x : y" if (semicolon->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 (tok->next()->str() == "false" || tok->next()->str() == "0") { // Use code after semicolon, remove code before it. Token::eraseTokens(tok, semicolon); 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 = semicolon; endTok; endTok = endTok->next()) { if (endTok->str() == "(" || endTok->str() == "[" || endTok->str() == "{") { endTok = endTok->link(); } else if (endTok->str() == "?") ++ternaryOplevel; else if (Token::Match(endTok, ")|}|]|;|,|:")) { if (endTok->str() == ":" && ternaryOplevel) --ternaryOplevel; else { Token::eraseTokens(semicolon->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, "%var% [ ]")) continue; tok = tok2->previous(); Token *end = tok2->next(); unsigned int count = 0; while (Token::Match(end, "[ ] [;=[]")) { end = end->tokAt(2); ++count; } if (Token::Match(end, "[;=]")) { do { tok2->deleteNext(2); tok->insertToken("*"); } while (--count); tok = end; } else tok = tok->tokAt(3); } } } void Tokenizer::simplifyFloatCasts() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok->next(), "( float|double ) %num%") && MathLib::isInt(tok->strAt(4))) { const bool isFloatType(tok->strAt(2) == "float"); tok->deleteNext(3); tok = tok->next(); // in case of type 'float', add the corresponding suffix 'f' tok->str(tok->str() + (isFloatType ? ".0f":".0")); } } } void Tokenizer::simplifyCasts() { for (Token *tok = list.front(); tok; tok = tok->next()) { // #2897 : don't remove cast in such cases: // *((char *)a + 1) = 0; // #3596 : remove cast when casting a function pointer: // (*(void (*)(char *))fp)(x); if (!tok->isName() && Token::simpleMatch(tok->next(), "* (") && !Token::Match(tok->linkAt(2), ") %var%") && !Token::simpleMatch(tok->linkAt(2), ") &")) { 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(), "( unsigned| %type% ) %num%") && tok->next()->link()->previous()->isStandardType()) { const MathLib::bigint value = MathLib::toLongNumber(tok->next()->link()->next()->str()); unsigned int bits = 8 * _typeSize[tok->next()->link()->previous()->str()]; if (!tok->tokAt(2)->isUnsigned()) bits--; if (bits < 31 && value >= 0 && value < (1LL << bits)) { Token::eraseTokens(tok, tok->next()->link()->next()); } continue; } while ((Token::Match(tok->next(), "( %type% *| *| *| ) *|&| %var%") && (tok->str() != ")" || tok->tokAt(2)->isStandardType())) || Token::Match(tok->next(), "( const| %type% * *| *| ) *|&| %var%") || Token::Match(tok->next(), "( const| %type% %type% *| *| *| ) *|&| %var%") || (!tok->isName() && (Token::Match(tok->next(), "( %type% * *| *| ) (") || Token::Match(tok->next(), "( const| %type% %type% * *| *| ) (")))) { if (tok->isName() && tok->str() != "return") break; if (tok->strAt(-1) == "operator") break; // Remove cast.. Token::eraseTokens(tok, tok->next()->link()->next()); // 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% * ) 0") || Token::Match(tok->next(), "( %type% %type% * ) 0")) { 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, "> (")) Token::eraseTokens(tok, tok2->next()); else break; } } } void Tokenizer::simplifyFunctionParameters() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{" || tok->str() == "[" || tok->str() == "(") { tok = tok->link(); } // Find the function e.g. foo( x ) or foo( x, y ) else if (Token::Match(tok, "%var% ( %var% [,)]") && !(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, "%var% [,)]")) { 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 (tok1->str() == "(" || tok1->str() == ")") { 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, "& %var% [ 0 ] !![")) continue; // Remove '[ 0 ]' suffix tok->next()->eraseTokens(tok->next(), tok->tokAt(5)); // Remove '&' prefix tok = tok->previous(); 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% *| *| ( * ) (") || Token::Match(tok, "( %type% %type% *| *| ( * ) (") || Token::Match(tok, "static_cast < %type% *| *| ( * ) (") || Token::Match(tok, "static_cast < %type% %type% *| *| ( * ) (")) { Token *tok1 = tok; if (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; 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, "(|:: * %var%")) continue; tok2 = tok2->tokAt(2); while (Token::Match(tok2, "%type%|:: %type%|::")) tok2 = tok2->next(); if (!Token::Match(tok2, "%var% ) (") && !Token::Match(tok2, "%var% [ ] ) (") && !(Token::Match(tok2, "%var% (") && Token::simpleMatch(tok2->linkAt(1), ") ) ("))) continue; while (tok->str() != "(") tok = tok->next(); // check that the declaration ends const Token *endTok = tok->link()->next()->link(); if (!Token::Match(endTok, ") ;|,|)|=|[|{")) continue; // 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, "%var% ( ) { return %bool%|%char%|%num%|%str% ; }") && tok->strAt(-1) != "::") { const Token* const any = tok->tokAt(5); const std::string pattern("(|[|=|%cop% " + tok->str() + " ( ) ;|]|)|%cop%"); for (Token *tok2 = list.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, pattern.c_str())) { tok2 = tok2->next(); tok2->str(any->str()); tok2->deleteNext(2); ret = true; } } } } return ret; } void Tokenizer::simplifyVarDecl(bool only_k_r_fpar) { simplifyVarDecl(list.front(), nullptr, only_k_r_fpar); } void Tokenizer::simplifyVarDecl(Token * tokBegin, Token * tokEnd, bool only_k_r_fpar) { // 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 (only_k_r_fpar && finishedwithkr) { if (tok->str() == "(" || tok->str() == "[" || tok->str() == "{") { 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->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:")) continue; Token *type0 = tok; if (!Token::Match(type0, "::|extern| %type%")) continue; if (Token::Match(type0, "else|return|public:|protected:|private:")) 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; tok2 = tok2->next(); ++typelen; } // strange looking variable declaration => don't split up. if (Token::Match(tok2, "%type% *| %var% , %type% *| %var%")) 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 (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| %var% ,|=" 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 ispointer = false; while (varName && varName->str() == "*") { ispointer = 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, "%var% ,|=")) { if (varName->str() != "operator") { tok2 = varName->next(); // The ',' or '=' token if (tok2->str() == "=" && (isstatic || (isconst && !ispointer))) { //do not split const non-pointer variables.. while (tok2 && tok2->str() != "," && tok2->str() != ";") { if (tok2->str() == "{" || tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); if (tok2->str() == "<" && TemplateSimplifier::templateParameters(tok2) > 0) tok2 = tok2->findClosingBracket(); tok2 = tok2->next(); } if (tok2 && tok2->str() == ";") tok2 = nullptr; } } else tok2 = nullptr; } //VLA case else if (Token::Match(varName, "%var% [")) { 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 (tok2->str() == "{" || tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); tok2 = tok2->next(); } if (tok2 && tok2->str() == ";") tok2 = nullptr; } } 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 (tok2->str() == "{" || tok2->str() == "(") tok2 = tok2->link(); else if (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(); 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() { enum { isLongLong, isLong, isInt } type; /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ if (_settings->sizeof_size_t == 8) { if (_settings->sizeof_long == 8) type = isLong; else type = isLongLong; } else if (_settings->sizeof_size_t == 4) { if (_settings->sizeof_long == 4) type = isLong; else type = isInt; } else return; for (Token *tok = list.front(); tok; tok = tok->next()) { bool inStd = false; if (Token::Match(tok, "std :: size_t|ssize_t|ptrdiff_t|intptr_t|uintptr_t")) { inStd = true; tok->deleteNext(); tok->deleteThis(); } else if (Token::Match(tok, ":: size_t|ssize_t|ptrdiff_t|intptr_t|uintptr_t")) { tok->deleteThis(); } if (Token::Match(tok, "size_t|uintptr_t|uintmax_t")) { if (inStd) tok->originalName("std::" + tok->str()); else tok->originalName(tok->str()); 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; } } else if (Token::Match(tok, "ssize_t|ptrdiff_t|intptr_t|intmax_t")) { if (inStd) tok->originalName("std::" + tok->str()); else tok->originalName(tok->str()); switch (type) { case isLongLong: tok->isLong(true); tok->str("long"); break; case isLong : tok->str("long"); break; case isInt: tok->str("int"); break; } } } if (_settings->platformType == Settings::Win32A || _settings->platformType == Settings::Win32W || _settings->platformType == Settings::Win64) { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->type() != Token::eType && tok->type() != Token::eName) continue; if (!tok->isUpperCaseName()) // All WinAPI types are uppercase. Reduce number of Token::Match calls by this condition. continue; if (Token::Match(tok, "BOOL|INT|INT32|HFILE|LONG32")) { tok->originalName(tok->str()); tok->str("int"); } else if (Token::Match(tok, "BOOLEAN|BYTE|UCHAR")) { tok->originalName(tok->str()); tok->str("char"); tok->isUnsigned(true); } else if (tok->str() == "CHAR") { tok->originalName(tok->str()); tok->str("char"); } else if (Token::Match(tok, "DWORD|ULONG|COLORREF|LCID|LCTYPE|LGRPID")) { tok->originalName(tok->str()); tok->str("long"); tok->isUnsigned(true); } else if (Token::Match(tok, "DWORD_PTR|ULONG_PTR|SIZE_T")) { tok->originalName(tok->str()); tok->str("long"); tok->isUnsigned(true); if (_settings->platformType == Settings::Win64) tok->isLong(true); } else if (tok->str() == "FLOAT") { tok->originalName(tok->str()); tok->str("float"); } else if (Token::Match(tok, "HRESULT|LONG")) { tok->originalName(tok->str()); tok->str("long"); } else if (tok->str() == "INT8") { tok->originalName(tok->str()); tok->str("char"); tok->isSigned(true); } else if (Token::Match(tok, "INT64|LONG64|LONGLONG")) { tok->originalName(tok->str()); tok->str("long"); tok->isLong(true); } else if (Token::Match(tok, "LONG_PTR|LPARAM|LRESULT|SSIZE_T")) { tok->originalName(tok->str()); tok->str("long"); if (_settings->platformType == Settings::Win64) tok->isLong(true); } else if (Token::Match(tok, "LPBOOL|PBOOL")) { tok->str("int"); tok->insertToken("*"); } else if (Token::Match(tok, "LPBYTE|PBOOLEAN|PBYTE|PUCHAR")) { tok->isUnsigned(true); tok->str("char"); tok->insertToken("*"); } else if (Token::Match(tok, "LPCSTR|PCSTR")) { tok->str("const"); tok->insertToken("*"); tok->insertToken("char"); } else if (tok->str() == "LPCVOID") { tok->str("const"); tok->insertToken("*"); tok->insertToken("void"); } else if (Token::Match(tok, "LPDWORD|LPCOLORREF|PDWORD|PULONG")) { tok->isUnsigned(true); tok->str("long"); tok->insertToken("*"); } else if (Token::Match(tok, "LPINT|PINT")) { tok->str("int"); tok->insertToken("*"); } else if (Token::Match(tok, "LPLONG|PLONG")) { tok->str("long"); tok->insertToken("*"); } else if (Token::Match(tok, "LPSTR|PSTR|PCHAR")) { tok->str("char"); tok->insertToken("*"); } else if (Token::Match(tok, "PWSTR|PWCHAR")) { tok->str("wchar_t"); tok->insertToken("*"); } else if (Token::Match(tok, "LPVOID|PVOID|HANDLE|HBITMAP|HBRUSH|HCOLORSPACE|HCURSOR|HDC|HFONT|HGDIOBJ|HGLOBAL|HICON|HINSTANCE|HKEY|HLOCAL|HMENU|HMETAFILE|HMODULE|HPALETTE|HPEN|HRGN|HRSRC|HWND|SERVICE_STATUS_HANDLE|SC_LOCK|SC_HANDLE|HACCEL|HCONV|HCONVLIST|HDDEDATA|HDESK|HDROP|HDWP|HENHMETAFILE|HHOOK|HKL|HMONITOR|HSZ|HWINSTA")) { tok->str("void"); tok->insertToken("*"); } else if ((tok->str() == "PHANDLE")) { tok->str("void"); tok->insertToken("*"); tok->insertToken("*"); } else if (Token::Match(tok, "LPWORD|PWORD|PUSHORT")) { tok->isUnsigned(true); tok->str("short"); tok->insertToken("*"); } else if (Token::Match(tok, "SHORT|INT16")) { tok->originalName(tok->str()); tok->str("short"); } else if (Token::Match(tok, "UINT|MMRESULT|SOCKET|ULONG32|UINT32|DWORD32")) { tok->originalName(tok->str()); tok->isUnsigned(true); tok->str("int"); } else if (Token::Match(tok, "UINT_PTR|WPARAM")) { tok->originalName(tok->str()); tok->isUnsigned(true); if (_settings->platformType == Settings::Win64) { tok->str("long"); tok->isLong(true); } else { tok->str("int"); } } else if (Token::Match(tok, "USHORT|WORD|ATOM|LANGID")) { tok->originalName(tok->str()); tok->isUnsigned(true); tok->str("short"); } else if (tok->str() == "VOID") { tok->originalName(tok->str()); tok->str("void"); } else if (tok->str() == "TCHAR") { tok->originalName(tok->str()); if (_settings->platformType == Settings::Win32A) tok->str("char"); else { tok->str("wchar_t"); } } else if (tok->str() == "TBYTE") { tok->originalName(tok->str()); tok->isUnsigned(true); if (_settings->platformType == Settings::Win32A) tok->str("short"); else tok->str("char"); } else if (Token::Match(tok, "PTSTR|LPTSTR")) { if (_settings->platformType == Settings::Win32A) { tok->str("char"); tok->insertToken("*"); } else { tok->str("wchar_t"); tok->insertToken("*"); } } else if (Token::Match(tok, "PCTSTR|LPCTSTR")) { tok->str("const"); if (_settings->platformType == Settings::Win32A) { tok->insertToken("*"); tok->insertToken("char"); } else { tok->insertToken("*"); tok->insertToken("wchar_t"); } } else if (Token::Match(tok, "ULONG64|DWORD64|ULONGLONG")) { tok->originalName(tok->str()); tok->isUnsigned(true); tok->isLong(true); tok->str("long"); } else if (tok->str() == "HALF_PTR") { tok->originalName(tok->str()); if (_settings->platformType == Settings::Win64) tok->str("int"); else tok->str("short"); } else if (tok->str() == "INT_PTR") { tok->originalName(tok->str()); if (_settings->platformType == Settings::Win64) { tok->str("long"); tok->isLong(true); } else { tok->str("int"); } } else if (tok->str() == "LPWSTR") { tok->str("wchar_t"); tok->insertToken("*"); } else if (tok->str() == "LPCWSTR") { tok->str("const"); tok->insertToken("*"); tok->insertToken("wchar_t"); } else if (tok->str() == "WCHAR") { tok->originalName(tok->str()); tok->str("wchar_t"); } } } } void Tokenizer::simplifyStdType() { for (Token *tok = list.front(); tok; tok = tok->next()) { // long unsigned => unsigned long if (Token::Match(tok, "char|short|int|long|__int8|__int16|__int32|__int64 unsigned|signed")) { bool isUnsigned = tok->next()->str() == "unsigned"; tok->deleteNext(); tok->isUnsigned(isUnsigned); tok->isSigned(!isUnsigned); } else if (!Token::Match(tok, "unsigned|signed|char|short|int|long|__int8|__int16|__int32|__int64")) continue; // check if signed or unsigned specified if (Token::Match(tok, "unsigned|signed")) { bool isUnsigned = tok->str() == "unsigned"; // unsigned i => unsigned int i if (!Token::Match(tok->next(), "char|short|int|long|__int8|__int16|__int32|__int64")) tok->str("int"); else tok->deleteThis(); tok->isUnsigned(isUnsigned); tok->isSigned(!isUnsigned); } if (tok->str() == "__int8") { tok->originalName(tok->str()); tok->str("char"); } else if (tok->str() == "__int16") { tok->originalName(tok->str()); tok->str("short"); } else if (tok->str() == "__int32") { tok->originalName(tok->str()); tok->str("int"); } else if (tok->str() == "__int64") { tok->originalName(tok->str()); tok->str("long"); tok->isLong(true); } else if (tok->str() == "int") { if (tok->strAt(1) == "long") { tok->str("long"); tok->deleteNext(); } else if (tok->strAt(1) == "short") { tok->str("short"); tok->deleteNext(); } if (tok->strAt(1) == "long") { tok->isLong(true); tok->deleteNext(); } if (Token::Match(tok->next(), "unsigned|signed")) { tok->isUnsigned(tok->next()->str() == "unsigned"); tok->isSigned(tok->next()->str() == "signed"); tok->deleteNext(); if (tok->strAt(1) == "long") tok->deleteNext(); else if (tok->strAt(1) == "short") tok->deleteNext(); } } else if (tok->str() == "long") { if (tok->strAt(1) == "long") { tok->isLong(true); tok->deleteNext(); } if (tok->strAt(1) == "int") { tok->deleteNext(); if (Token::Match(tok->next(), "unsigned|signed")) { tok->isUnsigned(tok->next()->str() == "unsigned"); tok->isSigned(tok->next()->str() == "signed"); tok->deleteNext(); } } else if (tok->strAt(1) == "double") { tok->str("double"); tok->isLong(true); tok->deleteNext(); } else if (Token::Match(tok->next(), "unsigned|signed")) { tok->isUnsigned(tok->next()->str() == "unsigned"); tok->isSigned(tok->next()->str() == "signed"); tok->deleteNext(); if (tok->strAt(1) == "int") tok->deleteNext(); } } else if (tok->str() == "short") { if (tok->strAt(1) == "int") tok->deleteNext(); if (Token::Match(tok->next(), "unsigned|signed")) { tok->isUnsigned(tok->next()->str() == "unsigned"); tok->isSigned(tok->next()->str() == "signed"); tok->deleteNext(); if (tok->strAt(1) == "int") tok->deleteNext(); } } } } void Tokenizer::simplifyStaticConst() { // This function will simplify the token list so that the qualifiers "static" // and "const" appear in the reverse order to what is in the array below. const char* qualifiers[] = {"const", "static"}; // 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++) { const char* qualifier = qualifiers[i]; for (Token *tok = list.front(); tok; tok = tok->next()) { // Keep searching for an instance of "static" or "const" if (!tok->next() || tok->next()->str() != qualifier) 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") || (isCPP() && Token::Match(leftTok, "private:|protected:|public:"))) break; } // The token preceding the declaration should indicate the start of a statement if (!leftTok || leftTok == tok || !Token::Match(leftTok, ";|{|}|private:|protected:|public:")) { continue; } // Move the qualifier to the left-most position in the declaration tok->deleteNext(); if (leftTok->next()) leftTok->next()->insertToken(qualifier, true); else leftTok->insertToken(qualifier); } } } void Tokenizer::simplifyIfAndWhileAssign() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok->next(), "if|while ( !| (| %var% =") && !Token::Match(tok->next(), "if|while ( !| (| %var% . %var% =")) 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(tok->strAt(2) == "!"); if (isNot) tok->next()->deleteNext(); // 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 "%var% = ..." 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()); tok3->next()->varId(tok2->varId()); Token *newTok = tok3->next(); newTok->fileIndex(tok2->fileIndex()); newTok->linenr(tok2->linenr()); // link() newly tokens manually if (Token::Match(newTok, "}|)|]")) { braces2.push(newTok); } else if (Token::Match(newTok, "{|(|[")) { Token::createMutualLinks(newTok, braces2.top()); braces2.pop(); } } } } } } void Tokenizer::simplifyVariableMultipleAssign() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%var% = %var% = %num%|%var% ;")) { // skip intermediate assignments Token *tok2 = tok->previous(); while (tok2 && tok2->str() == "=" && Token::Match(tok2->previous(), "%var%")) { tok2 = tok2->tokAt(-2); } if (!tok2 || tok2->str() != ";") { continue; } Token *stopAt = tok->tokAt(2); const Token *valueTok = tok->tokAt(4); const std::string value(valueTok->str()); tok2 = tok2->next(); while (tok2 != stopAt) { tok2->next()->insertToken(";"); tok2->next()->insertToken(value); tok2 = tok2->tokAt(4); } } } } void Tokenizer::simplifyIfNot() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|&&|%oror%")) { tok = tok->next(); while (tok && tok->str() == "(") tok = tok->next(); if (!tok) break; if (Token::Match(tok, "0|false == (") || Token::Match(tok, "0|false == %var%")) { tok->deleteNext(); tok->str("!"); } else if (Token::Match(tok, "%var% == 0|false")) { tok->deleteNext(2); tok = tok->previous(); tok->insertToken("!"); tok = tok->next(); } else if (Token::Match(tok, "%var% .|:: %var% == 0|false")) { tok = tok->previous(); tok->insertToken("!"); tok = tok->tokAt(4); tok->deleteNext(2); } else if (Token::Match(tok, "* %var% == 0|false")) { tok = tok->previous(); tok->insertToken("!"); tok = tok->tokAt(3); tok->deleteNext(2); } } else if (tok->link() && Token::Match(tok, ") == 0|false")) { // if( foo(x) == 0 ) if (Token::Match(tok->link()->tokAt(-2), "( %var%")) { tok->deleteNext(2); tok->link()->previous()->insertToken(tok->link()->previous()->str()); tok->link()->tokAt(-2)->str("!"); } // if( (x) == 0 ) else if (tok->link()->strAt(-1) == "(") { tok->deleteNext(2); tok->link()->insertToken("("); tok->link()->str("!"); Token *temp = tok->link(); Token::createMutualLinks(tok->link()->next(), tok); temp->link(0); } } } } void Tokenizer::simplifyIfNotNull() { for (Token *tok = list.front(); tok; tok = tok->next()) { Token *deleteFrom = nullptr; // Remove 'x = (x != 0)' if (Token::simpleMatch(tok, "= (")) { if (Token::Match(tok->tokAt(-2), "[;{}] %var%")) { const std::string varname(tok->previous()->str()); if (Token::simpleMatch(tok->tokAt(2), (varname + " != 0 ) ;").c_str()) || Token::simpleMatch(tok->tokAt(2), ("0 != " + varname + " ) ;").c_str())) { tok = tok->tokAt(-2); tok->deleteNext(8); } } continue; } if (Token::Match(tok, "==|!= (")) tok = tok->linkAt(1); if (Token::Match(tok, "(|&&|%oror%")) { tok = tok->next(); if (!tok) break; if (Token::simpleMatch(tok, "0 != (") || Token::Match(tok, "0 != %var%")) { deleteFrom = tok->previous(); if (tok->tokAt(2)) tok->tokAt(2)->isPointerCompare(true); } else if (Token::Match(tok, "%var% != 0")) { deleteFrom = tok; tok->isPointerCompare(true); tok->isExpandedMacro(tok->isExpandedMacro() || tok->tokAt(2)->isExpandedMacro()); } else if (Token::Match(tok, "%var% .|:: %var% != 0")) { tok = tok->tokAt(2); deleteFrom = tok; tok->isPointerCompare(true); tok->isExpandedMacro(tok->isExpandedMacro() || tok->tokAt(2)->isExpandedMacro()); } } else if (tok->link() && Token::simpleMatch(tok, ") != 0")) { deleteFrom = tok; } if (deleteFrom) { Token::eraseTokens(deleteFrom, deleteFrom->tokAt(3)); tok = deleteFrom; } } } void Tokenizer::simplifyIfSameInnerCondition() { // same inner condition for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "if ( %var% ) {")) { const unsigned int varid(tok->tokAt(2)->varId()); if (!varid) continue; for (Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) { if (tok2->str() == "{" || tok2->str() == "}") break; if (Token::simpleMatch(tok2, "if (")) { tok2 = tok2->tokAt(2); if (Token::Match(tok2, "%varid% )", varid)) tok2->str("true"); else if (Token::Match(tok2, "! %varid% )", varid)) tok2->next()->varId(varid); break; } } } } } // Binary operators simplification map static const std::pair cAlternativeTokens_[] = { std::make_pair("and", "&&"), std::make_pair("and_eq", "&="), std::make_pair("bitand", "&"), std::make_pair("bitor", "|"), std::make_pair("not_eq", "!="), std::make_pair("or", "||"), std::make_pair("or_eq", "|="), std::make_pair("xor", "^"), std::make_pair("xor_eq", "^=") }; static const std::map cAlternativeTokens(cAlternativeTokens_, cAlternativeTokens_ + sizeof(cAlternativeTokens_)/sizeof(*cAlternativeTokens_)); // Simplify the C alternative tokens: // and => && // and_eq => &= // bitand => & // bitor => | // compl => ~ // not => ! // not_eq => != // or => || // or_eq => |= // xor => ^ // xor_eq => ^= bool Tokenizer::simplifyCAlternativeTokens() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { Token *start = startOfExecutableScope(tok); if (start) { // Check for executable scope tok = start; Token * const end = tok->link(); for (Token *tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "%var%|%num%|)|] %any% %var%|%num%|(")) { const std::map::const_iterator cOpIt = cAlternativeTokens.find(tok2->next()->str()); if (cOpIt != cAlternativeTokens.end()) { tok2->next()->str(cOpIt->second); ret = true; } } if (Token::Match(tok2, "not|compl %var%|(") && !Token::Match(tok2->previous(), "[;{}]")) { // Don't simplify 'not p;' (in case 'not' is a type) tok2->str((tok2->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() { 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% *| %var% ( &| %any% ) ;")) { tok = initVar(tok); } else if (Token::Match(tok, "%type% *| %var% ( %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% *| %var% ( &| %any% ) ,")) { Token *tok1 = tok; 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%|* & %var% = %var% ;")) { 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| %var% = %any% ;") || Token::Match(tok, "static| const| static| %type% const| %var% ( %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 (tok->str() == "const" || tok->str() == "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; } } else if (tok->varId() && constantValues.find(tok->varId()) != constantValues.end()) { tok->str(constantValues[tok->varId()]); } } } // variable id for float/double variables std::set floatvars; // auto variables.. for (Token *tok = list.front(); tok; tok = tok->next()) { // Search for a block of code Token *start = startOfExecutableScope(tok); if (!start) continue; tok = start; // parse the block of code.. int indentlevel = 0; Token *tok2 = tok; for (; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}] float|double %var% ;")) { floatvars.insert(tok2->tokAt(2)->varId()); } if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { --indentlevel; if (indentlevel <= 0) break; } else if (tok2->previous()->str() != "*" && !Token::Match(tok2->tokAt(-2), "* --|++") && (Token::Match(tok2, "%var% = %bool%|%char%|%num%|%str%|%var% ;") || Token::Match(tok2, "%var% [ ] = %str% ;") || Token::Match(tok2, "%var% [ %num% ] = %str% ;") || Token::Match(tok2, "%var% = & %var% ;") || Token::Match(tok2, "%var% = & %var% [ 0 ] ;"))) { const unsigned int varid = tok2->varId(); if (varid == 0) 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(); while (Token::Match(tok3->previous(), ":: %type%")) tok3 = tok3->tokAt(-2); 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), "[;{}] %var% .") ? 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; ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, value, valueVarId, valueIsPointer, valueToken, indentlevel); } else if (Token::Match(tok2, "( %var% == %num% ) {")) { const unsigned int varid = tok2->next()->varId(); if (varid == 0) continue; const std::string structname; const Token *valueToken = tok2->tokAt(3); std::string value(tok2->strAt(3)), savedValue = value; const unsigned int valueVarId = 0; const bool valueIsPointer = false; // Insert a "%var% = %num% ;" at the beginning of the scope as simplifyKnownVariablesSimplify might compute an updated value Token *scopeStart = tok2->tokAt(5); scopeStart->insertToken(tok2->strAt(1)); scopeStart = scopeStart->next(); Token* artificialAssignment = scopeStart; scopeStart->insertToken("="); scopeStart = scopeStart->next(); scopeStart->insertToken(valueToken->str()); scopeStart = scopeStart->next(); scopeStart->insertToken(";"); scopeStart = scopeStart->next(); ret |= simplifyKnownVariablesSimplify(&artificialAssignment, tok2->tokAt(6), varid, structname, value, valueIsPointer, valueVarId, valueToken, -1); // Remove the artificial assignment if no modification was done if (artificialAssignment->strAt(2) == savedValue) { Token::eraseTokens(tok2->tokAt(5), scopeStart->next()); } } else if (Token::Match(tok2, "strcpy|sprintf ( %var% , %str% ) ;")) { const unsigned int varid(tok2->tokAt(2)->varId()); if (varid == 0) continue; const std::string structname; const Token * const valueToken = tok2->tokAt(4); std::string value(valueToken->str()); if (tok2->str() == "sprintf") { std::string::size_type n = std::string::npos; while ((n = value.find("%%",n+1)) != std::string::npos) { value.replace(n,2,"%"); } } const unsigned int valueVarId(0); const bool valueIsPointer(false); Token *tok3 = tok2->tokAt(6); ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, 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 std::string& compareop = tok2->strAt(5); if (compareop == "<") { value = tok2->strAt(6); valueVarId = tok2->tokAt(6)->varId(); } else value = MathLib::toString(MathLib::toLongNumber(tok2->strAt(6)) + 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, "& %var% [")); if (_errorLogger && !list.getFiles().empty()) _errorLogger->reportProgress(list.getFiles()[0], "Tokenize (simplifyKnownVariables)", tok3->progressValue()); #ifdef MAXTIME if (std::time(0) > maxtime) return false; #endif bool ret = false; Token* bailOutFromLoop = 0; 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% * %var% ; %var% = & %var% ;") && (*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 (tok3->str() == "break" || tok3->str() == "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 do is found if (tok3->str() == "do") break; // Stop if unknown function call is seen // If the variable is a global or a member variable it might be // changed by the function call // TODO: don't bail out if the variable is a local variable, // then it can't be changed by the function call. if (tok3->str() == ")" && tok3->link() && Token::Match(tok3->link()->tokAt(-2), "[;{}] %var% (") && !Token::Match(tok3->link()->previous(), "if|for|while|switch|BOOST_FOREACH")) break; // Stop if something like 'while (--var)' is found if (tok3->str() == "for" || tok3->str() == "while" || tok3->str() == "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 = 0; 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, "%var% = realloc ( %var% ,") && tok3->varId() == varid && tok3->tokAt(4)->varId() == varid) { tok3->tokAt(4)->str(value); ret = true; } // condition "(|&&|%OROR% %varid% )|&&|%OROR% if (!Token::Match(tok3->previous(), "( %var% )") && 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(), "[(,] %var% [,)]")) { // 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 (_settings->debugwarnings) { // 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(), ". %var% (")) break; // suppress debug-warning when assignment if (tok3->strAt(1) == "=") break; // taking address of variable.. if (Token::Match(tok3->tokAt(-2), "return|= & %var% ;")) break; // parameter in function call.. if (Token::Match(tok3->tokAt(-2), "%var% ( %var% ,|)") || Token::Match(tok3->previous(), ", %var% ,|)")) break; // conditional increment if (Token::Match(tok3->tokAt(-3), ") { ++|--") || Token::Match(tok3->tokAt(-2), ") { %var% ++|--")) 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, "& %var% ;")) { 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,"& %var% ;") && 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 (pointeralias && tok3->str() == "delete" && (Token::Match(tok3, "delete %varid% ;", varid) || Token::Match(tok3, "delete [ ] %varid%", varid))) { tok3 = (tok3->next() && 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, ("%var% ( " + 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, ("%var% ( %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, "%var%|::|*")) 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 + " %var% =").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, "& %var% ;"))) { tok3 = tok3->previous(); tok3->deleteThis(); ret = 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 (tok4->str() == "(" || tok4->str() == ")") break; // Replace variable used in condition.. if (Token::Match(tok4, "; %var% <|<=|!= %var% ; ++| %var% ++| )")) { 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 (tok->str() == "(" || tok->str() == "[" || (tok->str() == "{" && tok->previous() && tok->previous()->str() == "=")) tok = tok->link(); 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::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()->previous() == tok->next()->link()) { // We have "(( *something* ))", remove the inner // parentheses tok->deleteNext(); tok->link()->tokAt(-2)->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "! ( %var% )")) { // Remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "[(,;{}] ( %var% ) .")) { // Remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "[(,;{}] ( %var% (") && 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 [| ]| %var% ) ;")) { // 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(), "[(!*;{}] ( %var% )") && (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(), "[;{}[]().,!*] ( %var% .")) { Token *tok2 = tok->tokAt(2); while (Token::Match(tok2, ". %var%")) { 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%|%var%") || 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 !!%var%|)|>|>> ( %num%|%bool% ) %op%|;|,|)" if (Token::Match(tok, "( %bool%|%num% ) %cop%|;|,|)") && tok->strAt(-2) != "operator" && tok->previous() && !Token::Match(tok->previous(), "%var%|)") && (!(isCPP() && Token::Match(tok->previous(),">|>>")))) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "%type% ( * %var% ) [") && tok->previous()->isStandardType()) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "*|& ( %var% )")) { // We may have a variable declaration looking like "type_name *(var_name)" Token *tok2 = tok->tokAt(-2); while (tok2 && 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 <= (MathLib::bigint)Token::getStrLength(tok)) { tok->str(std::string("'" + Token::getCharAt(tok, (size_t)index) + "'")); tok->deleteNext(3); } } } } void Tokenizer::simplifyReference() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // starting executable scope.. Token *start = 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% & %var% (|= %var% )| ;")) { const unsigned int ref_id = tok2->tokAt(3)->varId(); if (!ref_id) continue; // replace reference in the code.. for (Token *tok3 = tok2->tokAt(7); tok3 && tok3 != end; tok3 = tok3->next()) { if (tok3->varId() == ref_id) { tok3->str(tok2->strAt(5)); tok3->varId(tok2->tokAt(5)->varId()); } } tok2->deleteNext(6+(tok->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(), "* ( %var% +|- %num%|%var% )")) { // 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::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 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(";"); } } void Tokenizer::duplicateEnumError(const Token * tok1, const Token * tok2, const std::string & type) const { if (tok1 && !(_settings->isEnabled("style"))) return; std::list locationList; locationList.push_back(tok1); locationList.push_back(tok2); const std::string tok2_str = tok2 ? tok2->str() : std::string("name"); reportError(locationList, Severity::style, "variableHidingEnum", std::string(type + " '" + tok2_str + "' hides enumerator with same name")); } // 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, const Token * name) 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() == "[") { 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")) return false; // look backwards if (tok->previous()->str() == "enum" || (Token::Match(tok->previous(), "%type%") && tok->previous()->str() != "return") || Token::Match(tok->tokAt(-2), "%type% &|*")) { duplicateEnumError(*tokPtr, name, "Function parameter"); // 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) { duplicateEnumError(*tokPtr, name, "Template parameter"); *tokPtr = end->link(); return true; } } } else { if (Token::Match(tok->previous(), "enum|,")) { duplicateEnumError(*tokPtr, name, "Variable"); 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"))) { duplicateEnumError(*tokPtr, name, "Variable"); return true; } } } } } return false; } class EnumValue { public: EnumValue() : name(nullptr), value(nullptr), start(nullptr), end(nullptr) { } EnumValue(const EnumValue &ev) { *this = ev; } EnumValue& operator=(const EnumValue& ev) { name=ev.name; value=ev.value; start=ev.start; end=ev.end; return *this; } EnumValue(Token *name_, Token *value_, Token *start_, Token *end_) : name(name_), value(value_), start(start_), end(end_) { } void simplify(const std::map &enumValues) { for (Token *tok = start; tok; tok = tok->next()) { if (enumValues.find(tok->str()) != enumValues.end()) { const EnumValue &other = enumValues.find(tok->str())->second; if (other.value != nullptr) tok->str(other.value->str()); else { const bool islast = (tok == end); Token *last = Tokenizer::copyTokens(tok, other.start, other.end); if (last == tok->next()) // tok->deleteThis() invalidates a pointer that points at the next token last = tok; tok->deleteThis(); if (islast) { end = last; } tok = last; } } if (tok == end) break; } // Simplify calculations.. while (start && start->previous() && TemplateSimplifier::simplifyNumericCalculations(start->previous())) { } if (Token::Match(start, "%num% [,}]")) { value = start; start = end = nullptr; } } Token *name; Token *value; Token *start; Token *end; }; void Tokenizer::simplifyEnum() { std::string className; int classLevel = 0; bool goback = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (goback) { //jump back once, see the comment at the end of the function goback = false; tok = tok->previous(); } if (tok && tok->next() && (!tok->previous() || (tok->previous()->str() != "enum")) && Token::Match(tok, "class|struct|namespace")) { className = tok->next()->str(); classLevel = 0; } else if (tok->str() == "}") { --classLevel; if (classLevel < 0) className = ""; } else if (tok->str() == "{") { ++classLevel; } else if (tok->str() == "enum") { Token *temp = tok->next(); if (!temp) break; if (Token::Match(temp, "class|struct")) temp = temp->next(); if (!Token::Match(temp, "[{:]") && (!temp->isName() || !Token::Match(temp->next(), "[{:;]"))) continue; Token *start = tok; Token *enumType = nullptr; Token *typeTokenStart = nullptr; Token *typeTokenEnd = nullptr; // check for C++11 enum class bool enumClass = isCPP() && Token::Match(tok->next(), "class|struct"); if (enumClass) tok->deleteNext(); // check for name if (tok->next()->isName()) { tok = tok->next(); enumType = tok; } // check for C++0x typed enumeration if (tok->next()->str() == ":") { tok = tok->next(); if (!tok->next() || !tok->next()->isName()) { syntaxError(tok); return; // can't recover } tok = tok->next(); typeTokenStart = tok; typeTokenEnd = typeTokenStart; while (typeTokenEnd->next() && (typeTokenEnd->next()->str() == "::" || Token::Match(typeTokenEnd->next(), "%type%"))) { typeTokenEnd = typeTokenEnd->next(); tok = tok->next(); } if (!tok->next()) { syntaxError(tok); return; // can't recover } } // check for forward declaration if (tok->next()->str() == ";") { tok = tok->next(); /** @todo start substitution check at forward declaration */ // delete forward declaration Token::eraseTokens(start, tok); start->deleteThis(); tok = start; continue; } else if (tok->next()->str() != "{") { syntaxError(tok->next()); return; } Token *tok1 = tok->next(); Token *end = tok1->link(); tok1 = tok1->next(); MathLib::bigint lastValue = -1; Token * lastEnumValueStart = 0; Token * lastEnumValueEnd = 0; // iterate over all enumerators between { and } // Give each enumerator the const value specified or if not specified, 1 + the // previous value or 0 if it is the first one. std::map enumValues; for (; tok1 && tok1 != end; tok1 = tok1->next()) { Token * enumName = 0; Token * enumValue = 0; Token * enumValueStart = 0; Token * enumValueEnd = 0; if (tok1->str() == "(") { tok1 = tok1->link(); continue; } if (Token::Match(tok1->previous(), ",|{ %type% ,|}")) { // no value specified enumName = tok1; ++lastValue; tok1->insertToken("="); tok1 = tok1->next(); if (lastEnumValueStart && lastEnumValueEnd) { // previous value was an expression Token *valueStart = tok1; tok1 = copyTokens(tok1, lastEnumValueStart, lastEnumValueEnd); // value is previous expression + 1 tok1->insertToken("+"); tok1 = tok1->next(); tok1->insertToken("1"); enumValue = 0; enumValueStart = valueStart->next(); enumValueEnd = tok1->next(); } else { // value is previous numeric value + 1 tok1->insertToken(MathLib::toString(lastValue)); enumValue = tok1->next(); } } else if (Token::Match(tok1->previous(), ",|{ %type% = %num% ,|}")) { // value is specified numeric value enumName = tok1; lastValue = MathLib::toLongNumber(tok1->strAt(2)); enumValue = tok1->tokAt(2); lastEnumValueStart = 0; lastEnumValueEnd = 0; } else if (Token::Match(tok1->previous(), ",|{ %type% =")) { // value is specified expression enumName = tok1; lastValue = 0; tok1 = tok1->tokAt(2); if (tok1->str() == "," || tok1->str() == "}") { syntaxError(tok1); break; } enumValueStart = tok1; enumValueEnd = tok1; while (enumValueEnd->next() && (!Token::Match(enumValueEnd->next(), "[},]"))) { if (Token::Match(enumValueEnd, "(|[")) { enumValueEnd = enumValueEnd->link(); continue; } else if (Token::Match(enumValueEnd, "%type% <") && isCPP() && TemplateSimplifier::templateParameters(enumValueEnd->next()) > 1U) { Token *endtoken = enumValueEnd->next(); do { endtoken = endtoken->next(); if (Token::Match(endtoken, "*|,|::|typename")) endtoken = endtoken->next(); if (endtoken->str() == "<" && TemplateSimplifier::templateParameters(endtoken)) endtoken = endtoken->findClosingBracket(); } while (Token::Match(endtoken, "%var%|%num% *| [,>]") || Token::Match(endtoken, "%var%|%num% ::|< %any%")); if (endtoken->str() == ">") { enumValueEnd = endtoken; if (Token::simpleMatch(endtoken, "> ( )")) enumValueEnd = enumValueEnd->next(); } else { syntaxError(enumValueEnd); return; } } enumValueEnd = enumValueEnd->next(); } // remember this expression in case it needs to be incremented lastEnumValueStart = enumValueStart; lastEnumValueEnd = enumValueEnd; // skip over expression tok1 = enumValueEnd; } // add enumerator constant.. if (enumName && (enumValue || (enumValueStart && enumValueEnd))) { EnumValue ev(enumName, enumValue, enumValueStart, enumValueEnd); ev.simplify(enumValues); enumValues[enumName->str()] = ev; lastEnumValueStart = ev.start; lastEnumValueEnd = ev.end; if (ev.start == nullptr) lastValue = MathLib::toLongNumber(ev.value->str()); tok1 = ev.end ? ev.end : ev.value; } } // Substitute enum values { if (!tok1) return; if (_settings->terminated()) return; std::string pattern; if (!className.empty()) pattern += className + " :: "; if (enumClass && enumType) pattern += enumType->str() + " :: "; int level = 0; bool inScope = !enumClass; // enum class objects are always in a different scope std::stack > shadowId; // duplicate ids in inner scope bool simplify = false; EnumValue *ev = nullptr; for (Token *tok2 = tok1->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "}") { --level; if (level < 0) inScope = false; if (!shadowId.empty()) shadowId.pop(); } else if (tok2->str() == "{") { // Is the same enum redefined? const Token *begin = end->link(); if (tok2->fileIndex() == begin->fileIndex() && tok2->linenr() == begin->linenr() && Token::Match(begin->tokAt(-2), "enum %type% {") && Token::Match(tok2->tokAt(-2), "enum %type% {") && begin->previous()->str() == tok2->previous()->str()) { // remove duplicate enum Token * startToken = tok2->tokAt(-3); tok2 = tok2->link()->next(); Token::eraseTokens(startToken, tok2); if (!tok2) break; } else { // Not a duplicate enum.. ++level; // Create a copy of the shadow ids for the inner scope if (!shadowId.empty()) shadowId.push(shadowId.top()); // are there shadow arguments? if (Token::simpleMatch(tok2->previous(), ") {") || Token::simpleMatch(tok2->tokAt(-2), ") const {")) { std::set shadowArg; for (const Token* arg = tok2; arg && arg->str() != "("; arg = arg->previous()) { if (Token::Match(arg->previous(), "%type%|*|& %type% [,)]") && enumValues.find(arg->str()) != enumValues.end()) { // is this a variable declaration const Token *prev = arg; while (Token::Match(prev,"%type%|*|&")) prev = prev->previous(); if (!Token::Match(prev,"[,(] %type%")) continue; if (prev->str() == "(" && (!Token::Match(prev->tokAt(-2), "%type%|::|*|& %type% (") || prev->strAt(-2) == "else")) continue; shadowArg.insert(arg->str()); if (inScope && _settings->isEnabled("style")) { const EnumValue enumValue = enumValues.find(arg->str())->second; duplicateEnumError(arg, enumValue.name, "Function argument"); } } } if (!shadowArg.empty()) { if (shadowId.empty()) shadowId.push(shadowArg); else shadowId.top().insert(shadowArg.begin(), shadowArg.end()); } } // are there shadow variables in the scope? std::set shadowVars; for (const Token *tok3 = tok2->next(); tok3 && tok3->str() != "}"; tok3 = tok3->next()) { if (tok3->str() == "{") { tok3 = tok3->link(); // skip inner scopes if (tok3 == nullptr) break; } else if (tok3->isName() && enumValues.find(tok3->str()) != enumValues.end()) { const Token *prev = tok3->previous(); if ((prev->isName() && !Token::Match(prev,"return|case|throw")) || Token::Match(prev, "&|* %type% =")) { // variable declaration? shadowVars.insert(tok3->str()); if (inScope && _settings->isEnabled("style")) { const EnumValue enumValue = enumValues.find(tok3->str())->second; duplicateEnumError(tok3, enumValue.name, "Variable"); } } } } if (!shadowVars.empty()) { if (shadowId.empty()) shadowId.push(shadowVars); else shadowId.top().insert(shadowVars.begin(), shadowVars.end()); } } // Function head } else if (Token::Match(tok2, "%var% (")) { const Token *prev = tok2->previous(); bool type = false; while (prev && (prev->isName() || Token::Match(prev, "*|&|::"))) { type |= (Token::Match(prev, "%type% !!::") && !Token::Match(prev, "throw|return")); prev = prev->previous(); } if (type && (!prev || Token::Match(prev, "[;{}]"))) { // skip ( .. ) tok2 = tok2->next()->link(); } } else if (!pattern.empty() && Token::Match(tok2, pattern.c_str())) { const Token* tok3 = tok2; while (tok3->strAt(1) == "::") tok3 = tok3->tokAt(2); if (enumValues.find(tok3->str()) != enumValues.end()) { simplify = true; ev = &(enumValues.find(tok3->str())->second); } } else if (inScope && // enum is in scope (shadowId.empty() || shadowId.top().find(tok2->str()) == shadowId.top().end()) && // no shadow enum/var/etc of enum enumValues.find(tok2->str()) != enumValues.end()) { // tok2 is a enum id with a known value ev = &(enumValues.find(tok2->str())->second); if (!duplicateDefinition(&tok2, ev->name)) { if (tok2->strAt(-1) == "::" || Token::Match(tok2->next(), "::|[|=")) { // Don't replace this enum if: // * it's preceded or followed by "::" // * it's followed by "[" or "=" } else { simplify = true; ev = &(enumValues.find(tok2->str())->second); } } else { // something with the same name. if (shadowId.empty()) shadowId.push(std::set()); shadowId.top().insert(tok2->str()); } } if (simplify) { if (ev->value) { tok2->str(ev->value->str()); while (tok2->strAt(1) == "::") tok2->deleteNext(2); } else { while (tok2->strAt(1) == "::") tok2->deleteNext(2); tok2 = tok2->previous(); tok2->deleteNext(); bool hasOp = false; int indentlevel = 0; for (const Token *enumtok = ev->start; enumtok != ev->end; enumtok = enumtok->next()) { if (enumtok->str() == "(") ++indentlevel; else if (enumtok->str() == ")") --indentlevel; if (indentlevel == 0) hasOp |= enumtok->isOp(); } if (!hasOp) tok2 = copyTokens(tok2, ev->start, ev->end); else { tok2->insertToken("("); Token *startPar = tok2->next(); tok2 = copyTokens(startPar, ev->start, ev->end); tok2->insertToken(")"); Token::createMutualLinks(startPar, tok2->next()); tok2 = tok2->next(); } } simplify = false; } } } // check for a variable definition: enum {} x; if (end->next() && end->next()->str() != ";") { Token *tempTok = end; tempTok->insertToken(";"); tempTok = tempTok->next(); if (typeTokenStart == 0) tempTok->insertToken("int"); else { Token *tempTok1 = typeTokenStart; tempTok->insertToken(tempTok1->str()); while (tempTok1 != typeTokenEnd) { tempTok1 = tempTok1->next(); tempTok->insertToken(tempTok1->str()); tempTok = tempTok->next(); } } } if (enumType) { const std::string pattern(className.empty() ? std::string("") : (className + " :: " + enumType->str())); // count { and } for tok2 int level = 0; bool inScope = true; bool exitThisScope = false; int exitScope = 0; bool simplify = false; bool hasClass = false; for (Token *tok2 = end->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "}") { --level; if (level < 0) inScope = false; if (exitThisScope) { if (level < exitScope) exitThisScope = false; } } else if (tok2->str() == "{") ++level; else if (!pattern.empty() && ((tok2->str() == "enum" && Token::Match(tok2->next(), pattern.c_str())) || Token::Match(tok2, pattern.c_str()))) { simplify = true; hasClass = true; } else if (inScope && !exitThisScope && (tok2->str() == enumType->str() || (tok2->str() == "enum" && tok2->next() && tok2->next()->str() == enumType->str()))) { if (tok2->strAt(-1) == "::") { // Don't replace this enum if it's preceded by "::" } else if (tok2->next() && (tok2->next()->isName() || tok2->next()->str() == "(")) { simplify = true; hasClass = false; } else if (tok2->previous()->str() == "(" && tok2->next()->str() == ")") { simplify = true; hasClass = false; } } if (simplify) { if (tok2->str() == "enum") tok2->deleteNext(); if (typeTokenStart == 0) tok2->str("int"); else { Token *tok3 = typeTokenStart; tok2->str(tok3->str()); while (tok3 != typeTokenEnd) { tok3 = tok3->next(); tok2->insertToken(tok3->str()); tok2 = tok2->next(); } } if (hasClass) { tok2->deleteNext(2); } simplify = false; } } } tok1 = start; Token::eraseTokens(tok1, end->next()); if (start != list.front()) { tok1 = start->previous(); tok1->deleteNext(); //no need to remove last token in the list if (tok1->tokAt(2)) tok1->deleteNext(); tok = tok1; } 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::simplifyStd() { if (isC()) return; std::set f; f.insert("strcat"); f.insert("strcpy"); f.insert("strncat"); f.insert("strncpy"); f.insert("free"); f.insert("malloc"); f.insert("strdup"); for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "std") continue; if (Token::Match(tok->previous(), "[(,{};] std :: %var% (") && f.find(tok->strAt(2)) != f.end()) { tok->deleteNext(); tok->deleteThis(); } } } //--------------------------------------------------------------------------- // Helper functions for handling the tokens list //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- bool Tokenizer::IsScopeNoReturn(const Token *endScopeToken, bool *unknown) const { std::string unknownFunc; bool ret = _settings->library.isScopeNoReturn(endScopeToken,&unknownFunc); if (unknown) *unknown = !unknownFunc.empty(); if (!unknownFunc.empty() && _settings->checkLibrary && _settings->isEnabled("information")) { 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 parentheses = 1; unsigned int parameter = 1; for (ftok = fpar; ftok; ftok = ftok->previous()) { if (ftok->str() == "(") { --parentheses; if (parentheses == 0) { break; } } else if (ftok->str() == ")") { ++parentheses; } else if (parentheses == 1 && ftok->str() == ",") { ++parameter; } else if (Token::Match(ftok, "[;{}]")) { break; } } // Is this a function call? if (ftok && Token::Match(ftok->tokAt(-2), "[;{}=] %var% (")) { const std::string functionName(ftok->previous()->str()); if (functionName == "return") return true; // Locate function declaration.. unsigned int indentlevel = 0; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() == "{") ++indentlevel; else if (tok->str() == "}") indentlevel = (indentlevel > 0) ? indentlevel - 1U : 0U; else if (indentlevel == 0 && 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; bool isgoto = Token::Match(begin->tokAt(-2), "goto %var% ;"); 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 = 0; 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, "[{};] %var% : ;") && 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' std::string labelpattern = "[{};] " + begin->previous()->str() + " : ;"; Token *start = tok->tokAt(2); if (start && start->str() == "(") start = start->link()->next(); if (start && start->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(); throw InternalError(tok, "syntax error", InternalError::SYNTAX); } void Tokenizer::syntaxError(const Token *tok, char c) const { printDebugOutput(); throw InternalError(tok, std::string("Invalid number of character (") + c + ") " + "when these macros are defined: '" + _configuration + "'.", 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::cppcheckError(const Token *tok) const { printDebugOutput(); throw InternalError(tok, "Analysis failed. If the code is valid then please report this failure.", InternalError::INTERNAL); } // ------------------------------------------------------------------------ // Helper function to check wether number is zero (0 or 0.0 or 0E+0) or not? // @param s --> a string to check // @return true in case s is zero and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isZeroNumber(const std::string &s) { const bool isInteger = MathLib::isInt(s); const bool isFloat = MathLib::isFloat(s); const bool isZeroValue = ((isInteger && (MathLib::toLongNumber(s) == 0L)) // case: integer number || (isFloat && MathLib::toString(MathLib::toDoubleNumber(s)) == "0.0")); // case: float number return isZeroValue; } // ------------------------------------------------------------------------ // Helper function to check wether number is one (1 or 0.1E+1 or 1E+0) or not? // @param s --> a string to check // @return true in case s is one and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isOneNumber(const std::string &s) { const bool isPositive = MathLib::isPositive(s); const bool isInteger = MathLib::isInt(s); const bool isFloat = MathLib::isFloat(s); const bool isZeroValue = ((isPositive && isInteger && (MathLib::toLongNumber(s) == 1L)) // case: integer number || (isPositive && isFloat && MathLib::toString(MathLib::toDoubleNumber(s)) == "1.0")); // case: float number return isZeroValue; } // ------------------------------------------------------------------------ // Helper function to check wether number is one (2 or 0.2E+1 or 2E+0) or not? // @param s --> a string to check // @return true in case s is two and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isTwoNumber(const std::string &s) { const bool isPositive = MathLib::isPositive(s); const bool isInteger = MathLib::isInt(s); const bool isFloat = MathLib::isFloat(s); const bool isZeroValue = ((isPositive && isInteger && (MathLib::toLongNumber(s) == 2L)) // case: integer number || (isPositive && isFloat && MathLib::toString(MathLib::toDoubleNumber(s)) == "2.0")); // case: float number return isZeroValue; } // ------------------------------------------------------ // 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/ // ------------------------------------------------------ bool Tokenizer::simplifyMathFunctions() { bool simplifcationMade = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isName() && tok->strAt(1) == "(") { // precondition for function if (Token::Match(tok, "atol ( %str% )")) { //@todo Add support for atoll() if (tok->previous() && 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; } // remove atol ( %num% tok->deleteNext(3); // Convert string into a number and insert into token list tok->str(MathLib::toString(MathLib::toLongNumber(strNumber))); simplifcationMade = true; } else if (Token::Match(tok, "abs|fabs|labs|llabs ( %num% )")) { if (tok->previous() && Token::simpleMatch(tok->tokAt(-2), "std ::")) { tok = tok->tokAt(-2);// set token index two steps back tok->deleteNext(2); // delete "std ::" } // get number string std::string strNumber(tok->strAt(2)); // is the string negative? if (!strNumber.empty() && strNumber[0] == '-') { strNumber = strNumber.substr(1); // remove '-' sign } tok->deleteNext(3); // delete e.g. abs ( 1 ) tok->str(strNumber); // insert result into token list simplifcationMade = true; } else if (Token::Match(tok, "fma|fmaf|fmal ( %any% , %any% , %any% )")) { // Simplify: fma(a,b,c) == > ( a ) * ( b ) + ( c ) // get parameters const std::string a(tok->strAt(2)); const std::string b(tok->strAt(4)); const std::string c(tok->strAt(6)); if (!a.empty() && !b.empty() && !c.empty()) { tok->deleteNext(7); // delete fma call tok->str("( " + a + " ) * ( " + b + " ) + ( " + c + " )"); // insert result into token list 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 (!strLeftNumber.empty() && !strRightNumber.empty() && isLessEqual) { tok->deleteNext(5); // delete e.g. fmin ( -1.0, 1.0 ) tok->str(strLeftNumber); // insert e.g. -1.0 simplifcationMade = true; } else { // case left > right ==> insert right tok->deleteNext(5); // delete e.g. fmin ( 1.0, 0.0 ) tok->str(strRightNumber); // insert e.g. 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 (!strLeftNumber.empty() && !strRightNumber.empty() && isLessEqual) { tok->deleteNext(5); // delete e.g. fmax ( -1.0, 1.0 ) tok->str(strRightNumber);// insert e.g. 1.0 simplifcationMade = true; } else { // case left > right ==> insert left tok->deleteNext(5); // delete e.g. fmax ( 1.0, 0.0 ) tok->str(strLeftNumber); // insert e.g. 1.0 simplifcationMade = true; } } else if (Token::Match(tok, "isgreater ( %num% , %num% )")) { // The isgreater(x,y) function is the same as calculating (x)>(y). // It returns true (1) if x is greater than y and false (0) otherwise. const std::string strLeftNumber(tok->strAt(2)); // get left number const std::string strRightNumber(tok->strAt(4)); // get right number if (!strRightNumber.empty() && !strLeftNumber.empty()) { const bool isGreater = MathLib::isGreater(strLeftNumber, strRightNumber); // compare numbers tok->deleteNext(5); // delete tokens tok->str((isGreater == true) ? "true": "false"); // insert results simplifcationMade = true; } } else if (Token::Match(tok, "isgreaterequal ( %num% , %num% )")) { // The isgreaterequal(x,y) function is the same as calculating (x)>=(y). // It returns true (1) if x is greater than or equal to y. // False (0) is returned otherwise. const std::string strLeftNumber(tok->strAt(2)); // get left number const std::string strRightNumber(tok->strAt(4)); // get right number if (!strRightNumber.empty() && !strLeftNumber.empty()) { const bool isGreaterEqual = MathLib::isGreaterEqual(strLeftNumber, strRightNumber); // compare numbers tok->deleteNext(5); // delete tokens tok->str((isGreaterEqual == true) ? "true": "false"); // insert results simplifcationMade = true; } } else if (Token::Match(tok, "isless ( %num% , %num% )")) { // Calling this function is the same as calculating (x)<(y). // It returns true (1) if x is less than y. // False (0) is returned otherwise. const std::string strLeftNumber(tok->strAt(2)); // get left number const std::string strRightNumber(tok->strAt(4)); // get right number if (!strRightNumber.empty() && !strLeftNumber.empty()) { const bool isLess = MathLib::isLess(strLeftNumber, strRightNumber); // compare numbers tok->deleteNext(5); // delete tokens tok->str((isLess == true) ? "true": "false"); // insert results simplifcationMade = true; } } else if (Token::Match(tok, "islessequal ( %num% , %num% )")) { // Calling this function is the same as calculating (x)<=(y). // It returns true (1) if x is less or equal to y. // False (0) is returned otherwise. const std::string strLeftNumber(tok->strAt(2)); // get left number const std::string strRightNumber(tok->strAt(4)); // get right number if (!strRightNumber.empty() && !strLeftNumber.empty()) { const bool isLessEqual = MathLib::isLessEqual(strLeftNumber, strRightNumber); // compare numbers tok->deleteNext(5); // delete tokens tok->str((isLessEqual == true) ? "true": "false"); // insert results simplifcationMade = true; } } else if (Token::Match(tok, "islessgreater ( %num% , %num% )")) { // Calling this function is the same as calculating (x)<(y) || (x)>(y). // It returns true (1) if x is less than y or x is greater than y. // False (0) is returned otherwise. const std::string strLeftNumber(tok->strAt(2)); // get left number const std::string strRightNumber(tok->strAt(4)); // get right number if (!strRightNumber.empty() && !strLeftNumber.empty()) { const bool isLessOrGreater(MathLib::isLess(strLeftNumber, strRightNumber) || MathLib::isGreater(strLeftNumber, strRightNumber)); // compare numbers tok->deleteNext(5); // delete tokens tok->str((isLessOrGreater == true) ? "true": "false"); // insert results simplifcationMade = true; } } else if (Token::Match(tok, "div|ldiv|lldiv ( %any% , %num% )")) { // Calling the function 'div(x,y)' is the same as calculating (x)/(y). In case y has the value 1 // (the identity element), the call 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 (!rightNumber.empty() && !leftParameter.empty()) { if (isOneNumber(rightNumber)) { tok->deleteNext(5); // delete tokens tok->str(leftParameter); // insert simplified result simplifcationMade = true; } } } else if (Token::Match(tok, "pow|powf|powl (")) { if (tok && 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 if (!leftNumber.empty() && !rightNumber.empty()) { 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 (tok && 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 (!rightNumber.empty() && !leftParameter.empty()) { if (isOneNumber(rightNumber)) { // case: x^(1) = x tok->deleteNext(5); // delete tokens tok->str(leftParameter); // insert simplified result simplifcationMade = true; } else if (isZeroNumber(rightNumber)) { // case: x^(0) = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } } } } } } // returns true if a simplifcation was performed and false otherwise. return simplifcationMade; } void Tokenizer::simplifyComma() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(" || tok->str() == "[" || (tok->str() == "{" && tok->previous() && tok->previous()->str() == "=")) { tok = tok->link(); continue; } // Skip unhandled template specifiers.. if (tok->link() && tok->str() == "<") tok = tok->link(); if (!tok->next() || tok->str() != ",") continue; // We must not accept just any keyword, e.g. accepting int // would cause function parameters to corrupt. if (tok->strAt(1) == "delete") { // Handle "delete a, delete b;" tok->str(";"); } if (Token::Match(tok->tokAt(-2), "delete %var% , %var% ;") && tok->next()->varId() != 0) { // Handle "delete a, b;" tok->str(";"); tok->insertToken("delete"); } else if (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 (Token::Match(tok2, "delete %var%") || Token::Match(tok2, "delete [ ] %var%")) { // Handle "delete a, a = 0;" replace = true; } else if (Token::Match(tok2, "[?:;,{}()]")) { if (replace && Token::Match(tok2, "[;{}]")) tok->str(";"); break; } } } bool inReturn = false; 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 (Token::Match(tok2, "[;{}?]")) { break; } else if (tok2->str() == ")" || tok2->str() == "]" || (tok2->str() == "}" && tok2->link()->previous() && tok2->link()->previous()->str() == "=")) { tok2 = tok2->link(); } else if (tok2->str() == "return" && Token::Match(tok2->previous(), "[;{}]")) { inReturn = true; startFrom = tok2->previous(); break; } } // find token where return ends and also count commas if (inReturn) { std::size_t commaCounter = 0; for (Token *tok2 = startFrom->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { endAt = tok2; break; } else if (tok2->str() == "(" || tok2->str() == "[" || (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 (tok2->str() == "(" || tok2->str() == "[" || (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::validate() const { std::stack linktok; 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); linktok.push(tok); } else if (Token::Match(tok, "[})]]") || (tok->str() == ">" && tok->link())) { if (tok->link() == nullptr) cppcheckError(tok); if (linktok.empty() == true) cppcheckError(tok); if (tok->link() != linktok.top()) cppcheckError(tok); if (tok != tok->link()->link()) cppcheckError(tok); linktok.pop(); } else if (tok->link() != nullptr) cppcheckError(tok); } if (!linktok.empty()) cppcheckError(linktok.top()); // Validate that the Tokenizer::list.back() is updated correctly during simplifications if (lastTok != list.back()) cppcheckError(lastTok); } 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::simplifyStructInit() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] struct| %type% %var% = { . %type% =")) { // Goto "." and check if the initializations have an expected format const Token *tok2 = tok; while (tok2->str() != ".") tok2 = tok2->next(); while (tok2 && tok2->str() == ".") { if (Token::Match(tok2, ". %type% = %num%|%var% [,}]")) tok2 = tok2->tokAt(4); else if (Token::Match(tok2, ". %type% = & %var% [,}]")) tok2 = tok2->tokAt(5); else break; if (Token::simpleMatch(tok2, ", .")) tok2 = tok2->next(); } if (!Token::simpleMatch(tok2, "} ;")) continue; // Known expression format => Perform simplification Token *vartok = tok->tokAt(3); if (vartok->str() == "=") vartok = vartok->previous(); vartok->next()->str(";"); Token *tok3 = vartok->tokAt(2); tok3->link(0); while (Token::Match(tok3, "[{,] . %type% =")) { tok3->str(vartok->str()); tok3->varId(vartok->varId()); tok3 = tok3->tokAt(5); while (!Token::Match(tok3, "[,}]")) tok3 = tok3->next(); if (tok3->str() == "}") { tok3->deleteThis(); break; } tok3->previous()->insertToken(";"); } } } } void Tokenizer::simplifyConst() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isStandardType() && tok->strAt(1) == "const") { tok->next()->str(tok->str()); tok->str("const"); } else if (Token::Match(tok, "struct %type% const")) { tok->tokAt(2)->str(tok->next()->str()); tok->str("const"); tok->next()->str("struct"); } else if (Token::Match(tok, "%type% const") && (!tok->previous() || Token::Match(tok->previous(), "[;{}(,]")) && tok->str().find(":") == std::string::npos && tok->str() != "operator") { tok->next()->str(tok->str()); tok->str("const"); } } } void Tokenizer::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) { Tokenizer t(settings, errorLogger); t.duplicateTypedefError(0, 0, "variable"); t.duplicateDeclarationError(0, 0, "variable"); t.duplicateEnumError(0, 0, "variable"); t.unnecessaryQualificationError(0, "type"); } 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 ( %var% = %num% ; %var% < %num% ;") && tok->strAt(2) == tok->strAt(6) && tok->strAt(4) == tok->strAt(8)) || (Token::Match(tok->previous(), "[{};] for ( %type% %var% = %num% ; %var% < %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(); end = end->next()->link(); tok = tok->previous(); eraseDeadCode(tok, end->next()); } } } void Tokenizer::simplifyErrNoInWhile() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "errno") continue; Token *endpar = nullptr; if (Token::Match(tok->previous(), "&& errno == EINTR ) { ;| }")) endpar = tok->tokAt(3); else if (Token::Match(tok->tokAt(-2), "&& ( errno == EINTR ) ) { ;| }")) endpar = tok->tokAt(4); else continue; if (Token::simpleMatch(endpar->link()->previous(), "while (")) { Token *tok1 = tok->previous(); if (tok1->str() == "(") tok1 = tok1->previous(); // erase "&& errno == EINTR" tok1 = tok1->previous(); Token::eraseTokens(tok1, endpar); // tok is invalid.. move to endpar tok = endpar; } } } void Tokenizer::simplifyFuncInWhile() { unsigned int count = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "while ( %var% ( %var% ) ) {")) 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() { // 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()) { // check for anonymous struct/union if (Token::Match(tok, "struct|union {")) { if (Token::Match(tok->next()->link(), "} *|&| %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 %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")) { 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 = 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% %var% ;")) tok2 = tok2->tokAt(3); else break; } if (!Token::simpleMatch(tok2, "} ;")) continue; Token *vartok = nullptr; tok2 = tok1->tokAt(2); while (Token::Match(tok2, "%type% %var% ;")) { 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() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "__cdecl|__stdcall|__fastcall|__thiscall|__clrcall|__syscall|__pascal|__fortran|__far|__near|WINAPI|APIENTRY|CALLBACK")) { tok->deleteThis(); } } } void Tokenizer::simplifyDeclspec() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::simpleMatch(tok, "__declspec (") && tok->next()->link() && tok->next()->link()->next()) { if (tok->strAt(2) == "nothrow") { Token *tok1 = tok->next()->link()->next(); while (tok1 && !Token::Match(tok1, "%var%")) { tok1 = tok1->next(); } if (tok1) { tok1->isDeclspecNothrow(true); } } Token::eraseTokens(tok, tok->next()->link()->next()); tok->deleteThis(); } } } void Tokenizer::simplifyAttribute() { for (Token *tok = list.front(); tok; tok = tok->next()) { 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()->str() == "void") // __attribute__((constructor)) void func() {} tok->next()->link()->next()->next()->isAttributeConstructor(true); else if (tok->next()->link()->next()->str() == ";" && tok->linkAt(-1)) // 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()->str() == "void") // __attribute__((destructor)) void func() {} tok->next()->link()->next()->next()->isAttributeDestructor(true); else if (tok->next()->link()->next()->str() == ";" && tok->linkAt(-1)) // 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__ )")) { // check if after variable name if (Token::Match(tok->next()->link()->next(), ";|=")) { if (Token::Match(tok->previous(), "%type%")) tok->previous()->isAttributeUnused(true); } // check if before variable name else if (Token::Match(tok->next()->link()->next(), "%type%")) tok->next()->link()->next()->isAttributeUnused(true); } else if (Token::Match(tok->tokAt(2), "( pure|__pure__ )")) { // type func(...) __attribute__((pure)); if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% (")) tok->previous()->link()->previous()->isAttributePure(true); // type __attribute__((pure)) func() { } else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2)->linkAt(2), ") %var% (")) tok->next()->link()->linkAt(2)->linkAt(2)->next()->isAttributePure(true); else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2), ") %var% (")) tok->next()->link()->linkAt(2)->next()->isAttributePure(true); else if (Token::Match(tok->next()->link(), ") %var% (")) tok->next()->link()->next()->isAttributePure(true); } else if (Token::Match(tok->tokAt(2), "( const|__const__ )")) { // type func(...) __attribute__((const)); if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% (")) tok->previous()->link()->previous()->isAttributeConst(true); // type __attribute__((const)) func() { } else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2)->linkAt(2), ") %var% (")) tok->next()->link()->linkAt(2)->linkAt(2)->next()->isAttributeConst(true); else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2), ") %var% (")) tok->next()->link()->linkAt(2)->next()->isAttributeConst(true); else if (Token::Match(tok->next()->link(), ") %var% (")) tok->next()->link()->next()->isAttributeConst(true); } else if (Token::Match(tok->tokAt(2), "( nothrow|__nothrow__")) { // type func(...) __attribute__((nothrow)); if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% (")) tok->previous()->link()->previous()->isAttributeNothrow(true); // type __attribute__((nothrow)) func() { } else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2)->linkAt(2), ") %var% (")) tok->next()->link()->linkAt(2)->linkAt(2)->next()->isAttributeNothrow(true); else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") && Token::Match(tok->next()->link()->linkAt(2), ") %var% (")) tok->next()->link()->linkAt(2)->next()->isAttributeNothrow(true); else if (Token::Match(tok->next()->link(), ") %var% (")) tok->next()->link()->next()->isAttributeNothrow(true); } Token::eraseTokens(tok, tok->next()->link()->next()); tok->deleteThis(); } } } // 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() { std::set keywords; keywords.insert("volatile"); keywords.insert("inline"); keywords.insert("_inline"); keywords.insert("__inline"); keywords.insert("__forceinline"); keywords.insert("register"); keywords.insert("__restrict"); keywords.insert("__restrict__"); // 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 (_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 (_settings->standards.cpp >= Standards::CPP11) { for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "constexpr") { tok->deleteThis(); } // final: // void f() final; <- function is final // struct name final { }; <- struct is final if (Token::Match(tok, ") final [{;]") || Token::Match(tok, "%type% final [:{]")) tok->deleteNext(); // override // void f() override; else if (Token::Match(tok, ") override [{;]")) tok->deleteNext(); else if (Token::Match(tok, ") const override [{;]")) tok->next()->deleteNext(); } } } 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, "[;{}] %var% ( %var% =") && Token::simpleMatch(tok->linkAt(2), ") ;") && !Token::Match(tok->next(), "assert|while")) { const std::string funcname(tok->next()->str()); const Token * const vartok = tok->tokAt(3); // Goto ',' or ')'.. for (Token *tok2 = tok->tokAt(4); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ";") break; else if (tok2->str() == ")" || tok2->str() == ",") { 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, "[;{}] %var% = ( {")) { // goto the "} )" unsigned int indentlevel = 0; Token *tok2 = tok; while (nullptr != (tok2 = tok2->next())) { if (tok2->str() == "(" || tok2->str() == "{") ++indentlevel; else if (tok2->str() == ")" || tok2->str() == "}") { if (indentlevel <= 2) break; --indentlevel; } } if (indentlevel == 2 && Token::simpleMatch(tok2, "} )")) { tok2 = tok2->tokAt(-3); if (Token::Match(tok2, "[;{}] %num%|%var% ;")) { 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")) { const Token *tok2 = tok; while (tok2 && tok2->linenr() == tok->linenr() && (tok2->isNumber() || tok2->isName() || tok2->str() == ",")) tok2 = tok2->next(); if (!tok2 || tok2->str() == ";" || tok2->linenr() != tok->linenr()) { instruction = tok->next()->stringifyList(tok2); Token::eraseTokens(tok, tok2); if (!tok2 || tok2->str() != ";") tok->insertToken(";"); } else continue; } else continue; // insert "asm ( "instruction" )" tok->str("asm"); 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); } } } } // 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: const| %type% %var% :") && !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 (Token::Match(tok, ";|{|}|public:|protected:|private: const| %type% : %any% ;") && 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()); } } } // Remove __builtin_expect(...), likely(...), and unlikely(...) void Tokenizer::simplifyBuiltinExpect() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok->next(), "__builtin_expect (")) { // Count parentheses for tok2 const Token* end = tok->linkAt(2); for (Token *tok2 = tok->tokAt(3); tok2 != end; tok2 = tok2->next()) { if (tok2->str() == "(") { tok2 = tok2->link(); } else if (tok2->str() == ",") { if (Token::Match(tok2, ", %num% )")) { tok->deleteNext(); tok2->deleteNext(); tok2->deleteThis(); } break; } } } else if (Token::Match(tok->next(), "likely|unlikely (")) { // remove closing ')' tok->linkAt(2)->deleteThis(); // remove "likely|unlikely (" tok->deleteNext(2); } } } // Add std:: in front of std classes, when using namespace std; was given void Tokenizer::simplifyNamespaceStd() { if (!isCPP()) return; static const char* stdTypes_[] = { // Types and objects in std namespace that are neither functions nor templates "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 stdTypes(stdTypes_, stdTypes_+sizeof(stdTypes_)/sizeof(*stdTypes_)); static const char* stdTemplates_[] = { "array", "basic_string", "bitset", "deque", "list", "map", "multimap", "priority_queue", "queue", "set", "multiset", "stack", "vector", "pair", "iterator", "iterator_traits", "unordered_map", "unordered_multimap", "unordered_set", "unordered_multiset", "tuple", "function" }; static const std::set stdTemplates(stdTemplates_, stdTemplates_+sizeof(stdTemplates_)/sizeof(*stdTemplates_)); static const char* stdFunctions_[] = { "getline", "for_each", "find", "find_if", "find_end", "find_first_of", "adjacent_find", "count", "count_if", "mismatch", "equal", "search", "search_n", "copy", "copy_backward", "swap", "swap_ranges", "iter_swap", "transform", "replace", "replace_if", "replace_copy", "replace_copy_if", "fill", "fill_n", "generate", "generate_n", "remove", "remove_if", "remove_copy", "remove_copy_if", "unique", "unique_copy", "reverse", "reverse_copy", "rotate", "rotate_copy", "random_shuffle", "partition", "stable_partition", "sort", "stable_sort", "partial_sort", "partial_sort_copy", "nth_element", "lower_bound", "upper_bound", "equal_range", "binary_search", "merge", "inplace_merge", "includes", "set_union", "set_intersection", "set_difference", "set_symmetric_difference", "push_heap", "pop_heap", "make_heap", "sort_heap", "min", "max", "min_element", "max_element", "lexicographical_compare", "next_permutation", "prev_permutation", "advance", "back_inserter", "distance", "front_inserter", "inserter", "make_pair", "make_shared", "make_tuple" }; static const std::set stdFunctions(stdFunctions_, stdFunctions_+sizeof(stdFunctions_)/sizeof(*stdFunctions_)); for (const Token* tok = Token::findsimplematch(list.front(), "using namespace std ;"); tok; tok = tok->next()) { bool insert = false; if (Token::Match(tok, "%var% (") && !Token::Match(tok->previous(), ".|::") && !Token::Match(tok->linkAt(1)->next(), "%var%|{") && stdFunctions.find(tok->str()) != stdFunctions.end()) insert = true; else if (Token::Match(tok, "%var% <") && !Token::Match(tok->previous(), ".|::") && stdTemplates.find(tok->str()) != stdTemplates.end()) insert = true; else if (tok->isName() && !tok->varId() && !Token::Match(tok->next(), "(|<") && !Token::Match(tok->previous(), ".|::") && 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 (_settings->standards.cpp == Standards::CPP11 && Token::Match(tok, "!!:: tr1 ::")) tok->next()->str("std"); } for (Token* tok = list.front(); tok; tok = tok->next()) { if (_settings->standards.cpp == Standards::CPP11 && 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(); } } } // Remove Microsoft MFC 'DECLARE_MESSAGE_MAP()' void Tokenizer::simplifyMicrosoftMFC() { // skip if not Windows if (!(_settings->platformType == Settings::Win32A || _settings->platformType == Settings::Win32W || _settings->platformType == Settings::Win64)) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok->next(), "DECLARE_MESSAGE_MAP ( )")) { tok->deleteNext(3); } else if (Token::Match(tok->next(), "DECLARE_DYNAMIC|DECLARE_DYNAMIC_CLASS|DECLARE_DYNCREATE ( %any% )")) { tok->deleteNext(4); } } } void Tokenizer::simplifyMicrosoftMemoryFunctions() { // skip if not Windows if (!(_settings->platformType == Settings::Win32A || _settings->platformType == Settings::Win32W || _settings->platformType == Settings::Win64)) return; for (Token *tok = list.front(); tok; tok = tok->next()) { 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) } } } void Tokenizer::simplifyMicrosoftStringFunctions() { // skip if not Windows if (_settings->platformType == Settings::Win32A) { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "_topen (")) { tok->str("open"); tok->originalName("_topen"); } else if (Token::simpleMatch(tok, "_tsopen_s (")) { tok->str("_sopen_s"); tok->originalName("_tsopen_s"); } else if (Token::simpleMatch(tok, "_tfopen (")) { tok->str("fopen"); tok->originalName("_tfopen"); } else if (Token::simpleMatch(tok, "_tfopen_s (")) { tok->str("fopen_s"); tok->originalName("_tfopen_s"); } else if (Token::simpleMatch(tok, "_tfreopen (")) { tok->str("_wfreopen"); tok->originalName("_tfreopen"); } else if (Token::simpleMatch(tok, "_tfreopen_s (")) { tok->str("_wfreopen_s"); tok->originalName("_tfreopen_s"); } else if (Token::simpleMatch(tok, "_tcscat (")) { tok->str("strcat"); tok->originalName("_tcscat"); } else if (Token::simpleMatch(tok, "_tcschr (")) { tok->str("strchr"); tok->originalName("_tcschr"); } else if (Token::simpleMatch(tok, "_tcscmp (")) { tok->str("strcmp"); tok->originalName("_tcscmp"); } else if (Token::simpleMatch(tok, "_tcsdup (")) { tok->str("strdup"); tok->originalName("_tcsdup"); } else if (Token::simpleMatch(tok, "_tcscpy (")) { tok->str("strcpy"); tok->originalName("_tcscpy"); } else if (Token::simpleMatch(tok, "_tcslen (")) { tok->str("strlen"); tok->originalName("_tcslen"); } else if (Token::simpleMatch(tok, "_tcsncat (")) { tok->str("strncat"); tok->originalName("_tcscat"); } else if (Token::simpleMatch(tok, "_tcsncpy (")) { tok->str("strncpy"); tok->originalName("_tcsncpy"); } else if (Token::simpleMatch(tok, "_tcsnlen (")) { tok->str("strnlen"); tok->originalName("_tcslen"); } else if (Token::simpleMatch(tok, "_tcsrchr (")) { tok->str("strrchr"); tok->originalName("_tcsrchr"); } else if (Token::simpleMatch(tok, "_tcsstr (")) { tok->str("strstr"); tok->originalName("_tcsstr"); } else if (Token::simpleMatch(tok, "_tcstok (")) { tok->str("strtok"); tok->originalName("_tcstok"); } else if (Token::simpleMatch(tok, "_ftprintf (")) { tok->str("fprintf"); tok->originalName("_ftprintf"); } else if (Token::simpleMatch(tok, "_tprintf (")) { tok->str("printf"); tok->originalName("_tprintf"); } else if (Token::simpleMatch(tok, "_stprintf (")) { tok->str("sprintf"); tok->originalName("_stprintf"); } else if (Token::simpleMatch(tok, "_sntprintf (")) { tok->str("_snprintf"); tok->originalName("_sntprintf"); } else if (Token::simpleMatch(tok, "_ftscanf (")) { tok->str("fscanf"); tok->originalName("_ftscanf"); } else if (Token::simpleMatch(tok, "_tscanf (")) { tok->str("scanf"); tok->originalName("_tscanf"); } else if (Token::simpleMatch(tok, "_stscanf (")) { tok->str("sscanf"); tok->originalName("_stscanf"); } else if (Token::simpleMatch(tok, "_ftprintf_s (")) { tok->str("fprintf_s"); tok->originalName("_ftprintf_s"); } else if (Token::simpleMatch(tok, "_tprintf_s (")) { tok->str("printf_s"); tok->originalName("_tprintf_s"); } else if (Token::simpleMatch(tok, "_stprintf_s (")) { tok->str("sprintf_s"); tok->originalName("_stprintf_s"); } else if (Token::simpleMatch(tok, "_sntprintf_s (")) { tok->str("_snprintf_s"); tok->originalName("_sntprintf_s"); } else if (Token::simpleMatch(tok, "_ftscanf_s (")) { tok->str("fscanf_s"); tok->originalName("_ftscanf_s"); } else if (Token::simpleMatch(tok, "_tscanf_s (")) { tok->str("scanf_s"); tok->originalName("_tscanf_s"); } else if (Token::simpleMatch(tok, "_stscanf_s (")) { tok->str("sscanf_s"); tok->originalName("_stscanf_s"); } else if (Token::Match(tok, "_T ( %char%|%str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); while (Token::Match(tok->next(), "_T ( %char%|%str% )")) { tok->next()->deleteNext(); tok->next()->deleteThis(); tok->next()->deleteNext(); tok->concatStr(tok->next()->str()); tok->deleteNext(); } } } } else if (_settings->platformType == Settings::Win32W || _settings->platformType == Settings::Win64) { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "_topen (")) { tok->str("_wopen"); tok->originalName("_topen"); } else if (Token::simpleMatch(tok, "_tsfopen_s (")) { tok->str("_wsopen_s"); tok->originalName("_tsopen_s"); } else if (Token::simpleMatch(tok, "_tfopen (")) { tok->str("_wfopen"); tok->originalName("_tfopen"); } else if (Token::simpleMatch(tok, "_tfopen_s (")) { tok->str("_wfopen_s"); tok->originalName("_tfopen_s"); } else if (Token::simpleMatch(tok, "_tfreopen (")) { tok->str("_wfreopen"); tok->originalName("_tfreopen"); } else if (Token::simpleMatch(tok, "_tfreopen_s (")) { tok->str("_wfreopen_s"); tok->originalName("_tfreopen_s"); } else if (Token::simpleMatch(tok, "_tcscat (")) { tok->str("wcscat"); tok->originalName("_tcscat"); } else if (Token::simpleMatch(tok, "_tcschr (")) { tok->str("wcschr"); tok->originalName("_tcschr"); } else if (Token::simpleMatch(tok, "_tcscmp (")) { tok->str("wcscmp"); tok->originalName("_tcscmp"); } else if (Token::simpleMatch(tok, "_tcscpy (")) { tok->str("wcscpy"); tok->originalName("_tcscpy"); } else if (Token::simpleMatch(tok, "_tcsdup (")) { tok->str("wcsdup"); tok->originalName("_tcsdup"); } else if (Token::simpleMatch(tok, "_tcslen (")) { tok->str("wcslen"); tok->originalName("_tcslen"); } else if (Token::simpleMatch(tok, "_tcsncat (")) { tok->str("wcsncat"); tok->originalName("_tcsncat"); } else if (Token::simpleMatch(tok, "_tcsncpy (")) { tok->str("wcsncpy"); tok->originalName("_tcsncpy"); } else if (Token::simpleMatch(tok, "_tcsnlen (")) { tok->str("wcsnlen"); tok->originalName("_tcsnlen"); } else if (Token::simpleMatch(tok, "_tcsrchr (")) { tok->str("wcsrchr"); tok->originalName("_tcsrchr"); } else if (Token::simpleMatch(tok, "_tcsstr (")) { tok->str("wcsstr"); tok->originalName("_tcsstr"); } else if (Token::simpleMatch(tok, "_tcstok (")) { tok->str("wcstok"); tok->originalName("_tcstok"); } else if (Token::simpleMatch(tok, "_ftprintf (")) { tok->str("fwprintf"); tok->originalName("_ftprintf"); } else if (Token::simpleMatch(tok, "_tprintf (")) { tok->str("wprintf"); tok->originalName("_tprintf"); } else if (Token::simpleMatch(tok, "_stprintf (")) { tok->str("swprintf"); tok->originalName("_stprintf"); } else if (Token::simpleMatch(tok, "_sntprintf (")) { tok->str("_snwprintf"); tok->originalName("_sntprintf"); } else if (Token::simpleMatch(tok, "_ftscanf (")) { tok->str("fwscanf"); tok->originalName("_ftscanf"); } else if (Token::simpleMatch(tok, "_tscanf (")) { tok->str("wscanf"); tok->originalName("_tscanf"); } else if (Token::simpleMatch(tok, "_stscanf (")) { tok->str("swscanf"); tok->originalName("_stscanf"); } else if (Token::simpleMatch(tok, "_ftprintf_s (")) { tok->str("fwprintf_s"); tok->originalName("_ftprintf_s"); } else if (Token::simpleMatch(tok, "_tprintf_s (")) { tok->str("wprintf_s"); tok->originalName("_tprintf_s"); } else if (Token::simpleMatch(tok, "_stprintf_s (")) { tok->str("swprintf_s"); tok->originalName("_stprintf_s"); } else if (Token::simpleMatch(tok, "_sntprintf_s (")) { tok->str("_snwprintf_s"); tok->originalName("_sntprintf_s"); } else if (Token::simpleMatch(tok, "_ftscanf_s (")) { tok->str("fwscanf_s"); tok->originalName("_ftscanf_s"); } else if (Token::simpleMatch(tok, "_tscanf_s (")) { tok->str("wscanf_s"); tok->originalName("_tscanf_s"); } else if (Token::simpleMatch(tok, "_stscanf_s (")) { tok->str("swscanf_s"); tok->originalName("_stscanf_s"); } else if (Token::Match(tok, "_T ( %char%|%str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); 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() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "( __closure * %var% )")) { 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 %var% :|{")) { 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() { 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 %var% (") && Token::simpleMatch(tok->linkAt(2), ") ;")) { tok->deleteThis(); } else if (!Token::Match(tok, "class %var% :")) 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() == "}") { --indentlevel; if (indentlevel == 0) break; } if (tok2->strAt(1) == "Q_OBJECT") { tok2->deleteNext(); } else 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 %var% (") && Token::simpleMatch(tok2->linkAt(3), ") ;")) { tok2->deleteNext(); } } } } void Tokenizer::createSymbolDatabase() { if (!_symbolDatabase) { _symbolDatabase = new SymbolDatabase(this, _settings, _errorLogger); // Set scope pointers for (std::list::iterator scope = _symbolDatabase->scopeList.begin(); scope != _symbolDatabase->scopeList.end(); ++scope) { Token* start = const_cast(scope->classStart); Token* end = const_cast(scope->classEnd); if (scope->type == Scope::eGlobal) { start = const_cast(list.front()); end = const_cast(list.back()); } if (start && end) { start->scope(&*scope); end->scope(&*scope); } if (start != end && start->next() != end) { for (Token* tok = start->next(); tok != end; tok = tok->next()) { if (tok->str() == "{") { bool break2 = false; for (std::list::const_iterator innerScope = scope->nestedList.begin(); innerScope != scope->nestedList.end(); ++innerScope) { if (tok == (*innerScope)->classStart) { // Is begin of inner scope tok = tok->link(); if (!tok || tok->next() == end || !tok->next()) { break2 = true; break; } tok = tok->next(); break; } } if (break2) break; } tok->scope(&*scope); } } } // Set function pointers for (Token* tok = list.front(); tok != list.back(); tok = tok->next()) { if (Token::Match(tok, "%var% (")) { tok->function(_symbolDatabase->findFunction(tok)); } } // Set variable pointers for (Token* tok = list.front(); tok != list.back(); tok = tok->next()) { if (tok->varId()) tok->variable(_symbolDatabase->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() && Token::Match(tok, "%var% [|.")) { Token *tok2 = tok->next(); // Locate "]" if (tok->next()->str() == "[") { while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); } Token *membertok = nullptr; if (Token::Match(tok2, ". %var%")) membertok = tok2->next(); else if (Token::Match(tok2, ") . %var%") && 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); } } } // check for function returning record type // func(...).var // func(...)[...].var else if (tok->function() && tok->next()->str() == "(" && (Token::Match(tok->next()->link(), ") . %var% !!(") || (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %var% !!(")))) { 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); } } } } } } } void Tokenizer::deleteSymbolDatabase() { // Clear scope, function, and variable pointers for (Token* tok = list.front(); tok != list.back(); tok = tok->next()) { tok->scope(0); tok->function(0); tok->variable(0); } delete _symbolDatabase; _symbolDatabase = 0; } static bool operatorEnd(const Token * tok) { if (tok && tok->str() == ")") { tok = tok->next(); while (tok && !Token::Match(tok, "[=;{),]")) { if (tok->str() == "const" || tok->str() == "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 && par->isName()) { op += par->str(); par = par->next(); // merge namespaces eg. 'operator std :: string () const {' if (Token::Match(par, ":: %var%|%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 (_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() && 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| {|;|:")) { std::string qualification = tok->str() + "::"; // check for extra qualification /** @todo this should be made more generic to handle more levels */ if (Token::Match(tok->tokAt(-2), "%type% ::")) { if (classInfo.size() >= 2) { if (classInfo[classInfo.size() - 2].className != tok->strAt(-2)) continue; else qualification = tok->strAt(-2) + "::" + qualification; } else continue; } if (_settings->isEnabled("portability")) unnecessaryQualificationError(tok, qualification); tok->deleteNext(); tok->deleteThis(); } } } } } void Tokenizer::unnecessaryQualificationError(const Token *tok, const std::string &qualification) const { reportError(tok, Severity::portability, "unnecessaryQualification", "The extra qualification \'" + qualification + "\' is unnecessary and is considered an error by many compilers."); } 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 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 && (tok->str() == "*" || tok->str() == "&")) break; name += tok->str(); if (Token::Match(tok, "struct|union")) 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"); } } } } } 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 { ErrorLogger::ErrorMessage errmsg(callstack, &list, severity, id, msg, inconclusive); if (_errorLogger) _errorLogger->reportErr(errmsg); else Check::reportError(errmsg); } void Tokenizer::setPodTypes() { if (_settings) { 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); } } } } cppcheck-1.66/lib/tokenize.h000066400000000000000000000564401236713773000160170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "errorlogger.h" #include "tokenlist.h" #include "config.h" #include #include #include #include class Settings; class SymbolDatabase; class TimerResults; /// @addtogroup Core /// @{ /** @brief The main purpose is to tokenize the source code. It also has functions that simplify the token list */ class CPPCHECKLIB Tokenizer { 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; /** * 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 * @param noSymbolDB_AST Disable creation of SymbolDatabase and AST * @return false if source code contains syntax errors */ bool tokenize(std::istream &code, const char FileName[], const std::string &configuration = emptyString, bool noSymbolDB_AST = false); /** * tokenize condition and run simple simplifications on it * @param code code * @return true if success. */ bool tokenizeCondition(const std::string &code); /** Set variable id */ void setVarId(); /** * Basic simplification of tokenlist * * @param FileName The filename to run; used to do * markup checks. * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList1(const char FileName[]); /** * Most aggressive simplification of tokenlist * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList2(); /** * 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 '* & ( %var% ) =' or any combination of '* &' and '()' * parentheses around '%var%' to '%var% =' */ 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; /** * get error messages that the tokenizer generate */ static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings); /** 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(); /** Insert array size where it isn't given */ void arraySize(); /** Simplify labels and 'case|default' syntaxes. */ void simplifyLabelsCaseDefault(); /** Remove macros in global scope */ void removeMacrosInGlobalScope(); /** 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(bool only_k_r_fpar); void simplifyVarDecl(Token * tokBegin, Token * tokEnd, 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(); /** * Collapse compound standard types into a single token. * unsigned long long int => long _isUnsigned=true,_isLong=true */ void simplifyStdType(); /** * 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 if-not * Example: "if(0==x);" => "if(!x);" */ void simplifyIfNot(); /** * simplify if-not NULL * Example: "if(0!=x);" => "if(x);" * Special case: 'x = (0 != x);' is removed. */ void simplifyIfNotNull(); /** @brief simplify if (a) { if (a) */ void simplifyIfSameInnerCondition(); /** * 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 float casts (float)1 => 1.0 */ void simplifyFloatCasts(); /** * 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 %var% { }'*/ 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 initialization */ void simplifyStructInit(); /** 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 combineStrings(); void concatenateDoubleSharp(); void simplifyFileAndLineMacro(); void simplifyNull(); void concatenateNegativeNumberAndAnyPositive(); void simplifyExternC(); void simplifyRoundCurlyParentheses(); void simplifyDebugNew(); void simplifySQL(); bool hasEnumsWithTypedef(); void simplifyDefaultAndDeleteInsideClass(); void findComplicatedSyntaxErrorsInTemplates(); /** * Simplify e.g. 'atol("0")' into '0' * @return true if simplifcations performed and false otherwise. */ bool 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); /** * Change "int const x;" into "const int x;" */ void simplifyConst(); /** * simplify "while (0)" */ void simplifyWhile0(); /** * Simplify while(func() && errno==EINTR) */ void simplifyErrNoInWhile(); /** * Simplify while(func(f)) */ void simplifyFuncInWhile(); /** * Replace enum with constant value */ void simplifyEnum(); /** * Remove "std::" before some function names */ void simplifyStd(); /** Simplify pointer to standard type (C only) */ void simplifyPointerToStandardType(); /** Simplify function pointers */ void simplifyFunctionPointers(); /** * Remove exception specifications. */ void removeExceptionSpecifications(); /** * 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(); /** Syntax error */ void syntaxError(const Token *tok) const; /** Syntax error. Example: invalid number of ')' */ void syntaxError(const Token *tok, char c) const; /** Report that there is an unhandled "class x y {" code */ void unhandled_macro_class_x_y(const Token *tok) const; /** * assert that tokens are ok - used during debugging for example * to catch problems in simplifyTokenList. */ void validate() const; /** * 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(); /** * Simplify bitfields - the field width is removed as we don't use it. */ void simplifyBitfields(); /** * Remove __builtin_expect(...), likely(...), and unlikely(...) */ void simplifyBuiltinExpect(); /** * Remove unnecessary member qualification */ void removeUnnecessaryQualification(); /** * unnecessary member qualification error */ void unnecessaryQualificationError(const Token *tok, const std::string &qualification) const; /** * Add std:: in front of std classes, when using namespace std; was given */ void simplifyNamespaceStd(); /** * Remove Microsoft MFC 'DECLARE_MESSAGE_MAP()' */ void simplifyMicrosoftMFC(); /** * 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(); /** * check for duplicate enum definition */ bool duplicateDefinition(Token **tokPtr, const Token *name) const; /** * 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; /** * duplicate enum definition error */ void duplicateEnumError(const Token *tok1, const Token *tok2, const std::string & type) const; bool duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef, bool undefinedStruct) const; void duplicateTypedefError(const Token *tok1, const Token *tok2, const std::string & type) const; /** * Report error - duplicate declarations */ void duplicateDeclarationError(const Token *tok1, const Token *tok2, const std::string &type) const; void unsupportedTypedef(const Token *tok) const; /** 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(); void printDebugOutput() const; void dump(std::ostream &out) const; Token *deleteInvalidTypedef(Token *typeDef); /** * Get variable count. * @return number of variables */ unsigned int varIdCount() const { return _varId; } /** * Simplify e.g. 'return(strncat(temp,"a",1));' into * strncat(temp,"a",1); return temp; */ void simplifyReturnStrncat(); /** * Output list of unknown types. */ void printUnknownTypes() const; /** * Token list: stores all tokens. */ TokenList list; // Implement tokens() as a wrapper for convinience when using the TokenList const Token* tokens() const { return list.front(); } /** * 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); /** * Helper function to check wether number is zero (0 or 0.0 or 0E+0) or not? * @param s --> a string to check * @return true in case is is zero and false otherwise. */ static bool isZeroNumber(const std::string &s); /** * Helper function to check wether number is one (1 or 0.1E+1 or 1E+0) or not? * @param s --> a string to check * @return true in case is is one and false otherwise. */ static bool isOneNumber(const std::string &s); /** * Helper function to check wether number is one (2 or 0.2E+1 or 2E+0) or not? * @param s --> a 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); private: /** Disable copy constructor, no implementation */ Tokenizer(const Tokenizer &); /** Disable assignment operator, no implementation */ Tokenizer &operator=(const Tokenizer &); static Token * startOfFunction(Token * tok); static Token * startOfExecutableScope(Token * tok) { return const_cast(startOfExecutableScope(const_cast(tok))); } /** 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.66/lib/tokenlist.cpp000066400000000000000000000730121236713773000165300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "token.h" #include "mathlib.h" #include "path.h" #include "preprocessor.h" #include "settings.h" #include "errorlogger.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(0), _back(0), _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 = 0; _back = 0; _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 static_cast(_files.size() - 1); } void TokenList::deleteTokens(Token *tok) { while (tok) { Token *next = tok->next(); delete tok; tok = next; } } //--------------------------------------------------------------------------- // add a token. //--------------------------------------------------------------------------- void TokenList::addtoken(const 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 std::string str2; if (MathLib::isHex(str) || MathLib::isOct(str) || MathLib::isBin(str)) { std::ostringstream str2stream; str2stream << MathLib::toLongNumber(str); str2 = str2stream.str(); } else if (str.compare(0, 5, "_Bool") == 0) { str2 = "bool"; } else { str2 = str; } if (_back) { _back->insertToken(str2); } else { _front = new Token(&_back); _back = _front; _back->str(str2); } 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 == 0) 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()); } //--------------------------------------------------------------------------- // 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->type(src->type()); 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); // line number in parsed code unsigned int lineno = 1; // The current token being parsed std::string CurrentToken; // lineNumbers holds line numbers for files in fileIndexes // every time an include file is completely parsed, last item in the vector // is removed and lineno is set to point to that value. std::stack lineNumbers; // fileIndexes holds index for _files vector about currently parsed files // every time an include file is completely parsed, last item in the vector // is removed and FileIndex is set to point to that value. std::stack fileIndexes; // FileIndex. What file in the _files vector is read now? unsigned int FileIndex = 0; bool expandedMacro = false; // Read one byte at a time from code and create tokens for (char ch = (char)code.get(); code.good() && ch; ch = (char)code.get()) { if (ch == Preprocessor::macroChar) { while (code.peek() == Preprocessor::macroChar) code.get(); if (!CurrentToken.empty()) { addtoken(CurrentToken, lineno, FileIndex, true); _back->isExpandedMacro(expandedMacro); } CurrentToken.clear(); expandedMacro = true; continue; } // char/string.. // multiline strings are not handled. The preprocessor should handle that for us. else if (ch == '\'' || ch == '\"') { std::string line; // read char bool special = false; char c = ch; do { // Append token.. line += c; // Special sequence '\.' if (special) special = false; else special = (c == '\\'); // Get next character c = (char)code.get(); } while (code.good() && (special || c != ch)); line += ch; // Handle #file "file.h" if (CurrentToken == "#file") { // Extract the filename line = line.substr(1, line.length() - 2); ++lineno; fileIndexes.push(FileIndex); FileIndex = appendFileIfNew(line); lineNumbers.push(lineno); lineno = 0; } else { // Add previous token addtoken(CurrentToken, lineno, FileIndex); if (!CurrentToken.empty()) _back->isExpandedMacro(expandedMacro); // Add content of the string addtoken(line, lineno, FileIndex); if (!line.empty()) _back->isExpandedMacro(expandedMacro); } CurrentToken.clear(); continue; } // Preprocessor should ensure code doesn't contain any extended ascii / utf / etc. assert(CurrentToken.empty() || (CurrentToken[0] & 0x80) == 0); if (ch == '.' && !CurrentToken.empty() && std::isdigit(CurrentToken[0])) { // Don't separate doubles "5.4" } else if (std::strchr("+-", ch) && CurrentToken.length() > 0 && std::isdigit(CurrentToken[0]) && (CurrentToken[CurrentToken.length()-1] == 'e' || CurrentToken[CurrentToken.length()-1] == 'E') && !MathLib::isHex(CurrentToken)) { // Don't separate doubles "4.2e+10" } else if (CurrentToken.empty() && ch == '.' && std::isdigit(code.peek())) { // tokenize .125 into 0.125 CurrentToken = "0"; } else if (std::strchr("+-*/%&|^?!=<>[](){};:,.~\n ", ch)) { if (CurrentToken == "#file") { // Handle this where strings are handled continue; } else if (CurrentToken == "#line") { // Read to end of line std::string line; std::getline(code, line); unsigned int row; std::istringstream fiss(line); if (fiss >> row) { // Update the current line number lineno = row; std::string line2; if (std::getline(fiss, line2) && line2.length() > 4U) { // _"file_name" -> file_name line2 = line2.substr(2, line2.length() - 3); // Update the current file FileIndex = appendFileIfNew(line2); } } else ++lineno; CurrentToken.clear(); continue; } else if (CurrentToken == "#endfile") { if (lineNumbers.empty() || fileIndexes.empty()) { // error deallocateTokens(); return false; } lineno = lineNumbers.top(); lineNumbers.pop(); FileIndex = fileIndexes.top(); fileIndexes.pop(); CurrentToken.clear(); continue; } addtoken(CurrentToken, lineno, FileIndex, true); if (!CurrentToken.empty()) { _back->isExpandedMacro(expandedMacro); expandedMacro = false; } CurrentToken.clear(); if (ch == '\n') { if (_settings->terminated()) return false; ++lineno; continue; } else if (ch == ' ') { continue; } CurrentToken += ch; // Add "++", "--", ">>" or ... token if (std::strchr("+-<>=:&|", ch) && (code.peek() == ch)) CurrentToken += (char)code.get(); addtoken(CurrentToken, lineno, FileIndex); _back->isExpandedMacro(expandedMacro); CurrentToken.clear(); expandedMacro = false; continue; } CurrentToken += ch; } addtoken(CurrentToken, lineno, FileIndex, true); if (!CurrentToken.empty()) _back->isExpandedMacro(expandedMacro); Token::assignProgressValues(_front); for (std::size_t i = 1; i < _files.size(); i++) _files[i] = Path::getRelativePath(_files[i], _settings->_basePaths); return true; } //--------------------------------------------------------------------------- struct AST_state { std::stack op; unsigned int depth; unsigned int inArrayAssignment; bool cpp; AST_state(bool cpp_) : depth(0), inArrayAssignment(0), cpp(cpp_) {} }; static bool iscast(const Token *tok) { if (!Token::Match(tok, "( %var%")) return false; if (tok->previous() && tok->previous()->isName()) return false; if (Token::Match(tok, "( (| typeof (") && Token::Match(tok->link(), ") %num%")) 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::Match(tok2, ") %any%") && (tok2->strAt(1) == "&" || (!tok2->next()->isOp() && !Token::Match(tok2->next(), "[[]);,?:.]")))); if (!Token::Match(tok2, "%var%|*|&|::")) return false; if (tok2->isStandardType()) type = true; } return false; } 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 && tok->str() == "." && (tok->strAt(-1) == "," || tok->strAt(-1) == "{")) // Jump over . in C style struct initialization tok = tok->next(); if (tok->isLiteral()) { state.op.push(tok); tok = tok->next(); } else if (tok->isName() && tok->str() != "case") { if (tok->str() == "return") { compileUnaryOp(tok, state, compileExpression); state.op.pop(); } else if (!state.cpp || !Token::Match(tok, "new|delete %var%|*|&|::|(|[")) { while (tok->next() && tok->next()->isName()) tok = tok->next(); state.op.push(tok); if (tok->next() && tok->linkAt(1) && Token::Match(tok, "%var% <")) tok = tok->linkAt(1); tok = tok->next(); } } else if (tok->str() == "{") { if (!state.inArrayAssignment && tok->strAt(-1) != "=") { state.op.push(tok); tok = tok->link()->next(); } else { if (tok->link() != tok->next()) { state.inArrayAssignment++; compileUnaryOp(tok, state, compileExpression); state.inArrayAssignment--; } else { state.op.push(tok); } } } } 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()) 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()->type() != Token::eIncDecOp || tok->type() == 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->type() == 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 (isPrefixUnary(tok, state.cpp) && tok->link()->strAt(1) == "(") { // 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 Token* squareBracket = tok; Token* roundBracket = squareBracket->link()->next(); Token* curlyBracket = Token::findsimplematch(roundBracket->link()->next(), "{"); if (!curlyBracket) break; tok = curlyBracket->next(); compileExpression(tok, state); state.op.push(roundBracket); compileUnaryOp(squareBracket, state, nullptr); tok = curlyBracket->link()->next(); } else { 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(); bool opPrevTopSquare = !state.op.empty() && state.op.top() && state.op.top()->str() == "["; std::size_t oldOpSize = state.op.size(); compileExpression(tok, state); tok = tok2; if ((tok->previous() && tok->previous()->isName() && (tok->strAt(-1) != "return" && (!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 break; } } static void compilePrecedence3(Token *&tok, AST_state& state) { compilePrecedence2(tok, state); while (tok) { if ((Token::Match(tok, "[+-!~*&]") || tok->type() == Token::eIncDecOp) && isPrefixUnary(tok, state.cpp)) { if (Token::Match(tok, "* [*,)]")) { Token* tok2 = tok; 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 %var%|::|(")) { Token* tok2 = tok; tok = tok->next(); state.op.push(tok); while (Token::Match(tok, "%var%|*|&|<|[")) { if (tok->link()) tok = tok->link(); tok = tok->next(); } compileUnaryOp(tok2, state, nullptr); } else if (state.cpp && Token::Match(tok, "delete %var%|*|&|::|(|[")) { Token* tok2 = tok; tok = tok->next(); if (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; 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->str() == "&") tok2 = tok2->next(); if (state.cpp && (tok2->str() == "," || tok2->str() == ")")) { 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) { // TODO: http://en.cppreference.com/w/cpp/language/operator_precedence says: // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." if (tok->isAssignmentOp() || Token::Match(tok, "[?:]")) { if (tok->str() == "?" && tok->strAt(1) == ":") { state.op.push(0); } compileBinOp(tok, state, compileAssignTernary); } else break; } } static void compileComma(Token *&tok, AST_state& state) { compileAssignTernary(tok, state); while (tok) { if (tok->str() == ",") { 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 Token * createAstAtToken(Token *tok, bool cpp) { if (Token::simpleMatch(tok,"for (")) { Token *tok2 = tok->tokAt(2); Token *init1 = nullptr; const 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, "%var% %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) { init1 = tok2; AST_state state1(cpp); compileExpression(tok2, state1); if (tok2->str() == ";" || tok2->str() == ")") break; init1 = 0; } 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; tok2 = tok2->next(); AST_state state3(cpp); compileExpression(tok2, state3); if (init != semicolon1) semicolon1->astOperand1(const_cast(init->astTop())); tok2 = semicolon1->next(); while (tok2 != semicolon2 && !tok2->isName() && !tok2->isNumber()) tok2 = tok2->next(); if (tok2 != semicolon2) semicolon2->astOperand1(const_cast(tok2->astTop())); tok2 = tok->linkAt(1); while (tok2 != semicolon2 && !tok2->isName() && !tok2->isNumber()) tok2 = tok2->previous(); if (tok2 != semicolon2) semicolon2->astOperand2(const_cast(tok2->astTop())); semicolon1->astOperand2(semicolon2); tok->next()->astOperand1(tok); tok->next()->astOperand2(semicolon1); return tok->linkAt(1); } if (Token::simpleMatch(tok, "( {")) return tok; if (Token::Match(tok, "%type% <") && Token::Match(tok->linkAt(1), "> !!(")) return tok->linkAt(1); if (tok->str() == "return" || !tok->previous() || Token::Match(tok, "%var% %op%|(|[|.|::") || Token::Match(tok->previous(), "[;{}] %cop%|++|--|( !!{")) { Token * const tok1 = tok; AST_state state(cpp); compileExpression(tok, state); Token * const endToken = tok; if (endToken == tok1) return tok1; // Compile inner expressions inside inner ({..}) for (tok = tok1->next(); tok && tok != endToken; tok = tok ? tok->next() : NULL) { if (!Token::simpleMatch(tok, "( {")) continue; if (tok->next() == endToken) break; const Token * const endToken2 = tok->linkAt(1); for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : NULL) tok = createAstAtToken(tok, cpp); } return endToken ? endToken->previous() : NULL; } return tok; } void TokenList::createAst() { for (Token *tok = _front; tok; tok = tok ? tok->next() : NULL) { tok = createAstAtToken(tok, isCPP()); } } 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(); } cppcheck-1.66/lib/tokenlist.h000066400000000000000000000100431236713773000161700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include "config.h" class Token; class Settings; /// @addtogroup Core /// @{ class CPPCHECKLIB TokenList { public: TokenList(const Settings* settings); ~TokenList(); void setSettings(const Settings *settings) { _settings = 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(const 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); /** * 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); /** Deallocate list */ void deallocateTokens(); /** append file name if seen the first time; return its index in any case */ unsigned int appendFileIfNew(const std::string &file); /** 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; void createAst(); private: /** Disable copy constructor, no implementation */ TokenList(const TokenList &); /** Disable assignment operator, no implementation */ TokenList &operator=(const TokenList &); public: private: /// private /** 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.66/lib/valueflow.cpp000066400000000000000000001640071236713773000165250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "errorlogger.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenlist.h" #include static void execute(const Token *expr, std::map * const programMemory, MathLib::bigint *result, bool *error); static void bailout(TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what) { std::list callstack; callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist)); ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "ValueFlow bailout: " + what, "valueFlowBailout", false); errorLogger->reportErr(errmsg); } static bool bailoutFunctionPar(const Token *tok, const ValueFlow::Value &value, const Settings *settings, bool *inconclusive) { if (!tok) return false; // address of variable const bool addressOf = tok && Token::simpleMatch(tok->previous(), "&"); // passing variable to subfunction? if (Token::Match(tok->tokAt(-2), ") & %var% [,)]") && Token::Match(tok->linkAt(-2)->previous(), "[,(] (")) ; else if (Token::Match(tok->tokAt(addressOf?-2:-1), "[(,] &| %var% [,)]")) ; else return false; // reinterpret_cast etc.. if (Token::Match(tok->tokAt(-3), "> ( & %var% ) [,)]") && 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 (!Token::Match(tok,"%var% (")) return false; // not a function => do not bailout if (!tok->function()) { // if value is 0 and the library says 0 is invalid => do not bailout if (value.intvalue==0 && settings->library.isnullargbad(tok->str(), 1+argnr)) return false; // addressOf => inconclusive if (!addressOf) { *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(); } /** * Is condition always false when variable has given value? * \param condition top ast token in condition * \param varid variable id for variable * \param value value of variable */ static bool conditionIsFalse(const Token *condition, const std::map &programMemory) { if (!condition) return false; if (condition->str() == "&&") { bool result1 = conditionIsFalse(condition->astOperand1(), programMemory); bool result2 = result1 ? true : conditionIsFalse(condition->astOperand2(), programMemory); return result2; } std::map 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 varid variable id for variable * \param value value of variable */ static bool conditionIsTrue(const Token *condition, const std::map &programMemory) { if (!condition) return false; if (condition->str() == "||") { bool result1 = conditionIsTrue(condition->astOperand1(), programMemory); bool result2 = result1 ? true : conditionIsTrue(condition->astOperand2(), programMemory); return result2; } std::map progmem(programMemory); bool error = false; MathLib::bigint result; execute(condition, &progmem, &result, &error); return !error && result == 1; } /** * Get program memory by looking backwards from given token. */ static std::map getProgramMemory(const Token *tok, unsigned int varid, const ValueFlow::Value &value) { std::map programMemory; programMemory[varid] = value.intvalue; const std::map programMemory1(programMemory); int indentlevel = 0; for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2, "[;{}] %var% = %num% ;")) { const Token *vartok = tok2->next(); const Token *numtok = tok2->tokAt(3); if (vartok->varId() != 0U && programMemory.find(vartok->varId()) == programMemory.end()) programMemory[vartok->varId()] = MathLib::toLongNumber(numtok->str()); } 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) continue; if (tok2 != valuetok && tok2->str() == valuetok->str()) 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 bool isVariableChanged(const Token *start, const Token *end, const unsigned int varid) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->varId() == varid) { if (Token::Match(tok, "%var% =")) return true; const Token *parent = tok->astParent(); while (parent && parent->str() == ".") parent = parent->astParent(); if (parent && parent->type() == Token::eIncDecOp) return true; } } return false; } /** set ValueFlow value and perform calculations if possible */ static void setTokenValue(Token* tok, const ValueFlow::Value &value) { // if value already exists, don't add it again std::list::iterator it; for (it = tok->values.begin(); it != tok->values.end(); ++it) { if (it->intvalue == value.intvalue) { if (it->inconclusive && !value.inconclusive) { *it = value; break; } return; } } if (it == tok->values.end()) { tok->values.push_back(value); it = tok->values.end(); --it; if (it->varId == 0) it->varId = tok->varId(); } Token *parent = const_cast(tok->astParent()); // Cast.. if (parent && parent->str() == "(" && tok == parent->link()->next()) { setTokenValue(parent,value); } // Calculations.. else if (parent && parent->isArithmeticalOp() && parent->astOperand1() && parent->astOperand2()) { std::list::const_iterator value1, value2; for (value1 = parent->astOperand1()->values.begin(); value1 != parent->astOperand1()->values.end(); ++value1) { for (value2 = parent->astOperand2()->values.begin(); value2 != parent->astOperand2()->values.end(); ++value2) { 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.inconclusive = value1->inconclusive | value2->inconclusive; result.varId = (value1->varId != 0U) ? value1->varId : value2->varId; result.varvalue = (result.varId == value1->varId) ? value1->intvalue : value2->intvalue; switch (parent->str()[0]) { case '+': result.intvalue = value1->intvalue + value2->intvalue; setTokenValue(parent, result); break; case '-': result.intvalue = value1->intvalue - value2->intvalue; setTokenValue(parent, result); break; case '*': result.intvalue = value1->intvalue * value2->intvalue; setTokenValue(parent, result); break; case '/': if (value2->intvalue == 0) break; result.intvalue = value1->intvalue / value2->intvalue; setTokenValue(parent, result); break; case '%': if (value2->intvalue == 0) break; result.intvalue = value1->intvalue % value2->intvalue; setTokenValue(parent, result); break; } } } } } } static void valueFlowNumber(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->isNumber() && MathLib::isInt(tok->str())) setTokenValue(tok, ValueFlow::Value(MathLib::toLongNumber(tok->str()))); } } static void valueFlowBitAnd(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->str() != "&") 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 <= 60 && ((1LL<front(); tok; tok = tok->next()) { unsigned int varid=0; MathLib::bigint num=0; const Variable *var=0; if (tok->isComparisonOp() && tok->astOperand1() && tok->astOperand2()) { if (tok->astOperand1()->isName() && tok->astOperand2()->isNumber()) { varid = tok->astOperand1()->varId(); var = tok->astOperand1()->variable(); num = MathLib::toLongNumber(tok->astOperand2()->str()); } else if (tok->astOperand1()->isNumber() && tok->astOperand2()->isName()) { varid = tok->astOperand2()->varId(); var = tok->astOperand2()->variable(); num = MathLib::toLongNumber(tok->astOperand1()->str()); } else { continue; } } else if (Token::Match(tok->previous(), "if|while ( %var% %oror%|&&|)") || Token::Match(tok, "%oror%|&& %var% %oror%|&&|)")) { varid = tok->next()->varId(); var = tok->next()->variable(); num = 0; } else if (tok->str() == "!" && tok->astOperand1() && tok->astOperand1()->isName()) { varid = tok->astOperand1()->varId(); var = tok->astOperand1()->variable(); num = 0; } else { continue; } if (varid == 0U || !var) continue; // bailout: global non-const variables if (!(var->isLocal() || var->isArgument()) && !var->isConst()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "global variable " + var->nameToken()->str()); 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; 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)) { varid = 0U; if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable " + var->nameToken()->str() + " 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->nameToken()->str() + ", condition is defined in macro"); } } if (varid == 0U) continue; // extra logic for unsigned variables 'i>=1' => possible value can also be 0 ValueFlow::Value val(tok, num); val.varId = varid; ValueFlow::Value val2; if (num==1U && Token::Match(tok,"<=|>=")) { bool isunsigned = false; for (const Token* type = var->typeStartToken(); type && type->varId() == 0U; type = type->next()) isunsigned |= type->isUnsigned(); if (isunsigned) { val2 = ValueFlow::Value(tok,0); val2.varId = varid; } } if (Token::Match(tok,"<|>")) { if (num!=0) continue; bool isunsigned = false; for (const Token* type = var->typeStartToken(); type && type->varId() == 0U; type = type->next()) isunsigned |= type->isUnsigned(); if (!isunsigned) continue; } for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) { if (!tok2) { if (settings->debugwarnings) { std::list callstack; callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist)); ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "iterated too far", "debugValueFlowBeforeCondition", false); errorLogger->reportErr(errmsg); } break; } if (tok2->varId() == varid) { // bailout: assignment if (Token::Match(tok2->previous(), "!!* %var% =")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str()); break; } // increment/decrement if (Token::Match(tok2->previous(), "[;{}] %var% ++|-- ;")) val.intvalue += (tok2->strAt(1)=="++") ? -1 : 1; else if (Token::Match(tok2->tokAt(-2), "[;{}] ++|-- %var% ;")) val.intvalue += (tok2->strAt(-1)=="++") ? -1 : 1; else if (Token::Match(tok2->previous(), "++|-- %var%") || Token::Match(tok2, "%var% ++|--")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "increment/decrement of " + tok2->str()); break; } // 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,"%var%|.") && prev->str() != "sizeof") prev = prev->previous(); if (prev && prev->str() == "sizeof") continue; } // assigned by subfunction? bool inconclusive = false; if (bailoutFunctionPar(tok2,val2.condition ? val2 : val, settings, &inconclusive)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction"); break; } val.inconclusive |= inconclusive; val2.inconclusive |= 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); if (val2.condition) setTokenValue(tok2,val2); if (var && tok2 == var->nameToken()) break; } // skip sizeof.. if (tok2->str() == ")" && Token::Match(tok2->link()->previous(), "typeof|sizeof (")) tok2 = tok2->link(); // goto label if (Token::Match(tok2, "[;{}] %var% :")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2->next(), "variable " + var->nameToken()->str() + " stopping on goto label"); break; } if (tok2->str() == "}") { const Token *vartok = Token::findmatch(tok2->link(), "%varid%", tok2, varid); while (Token::Match(vartok, "%var% = %num% ;") && !vartok->tokAt(2)->getValue(num)) vartok = Token::findmatch(vartok->next(), "%varid%", tok2, varid); if (vartok) { if (settings->debugwarnings) { std::string errmsg = "variable "; if (var) errmsg += var->nameToken()->str() + " "; 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)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " 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 (Token::simpleMatch(tok2->previous(), ") {") && !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->nameToken()->str() + " stopping on " + parent->str()); break; } } } } } static bool valueFlowForward(Token * const startToken, const Token * const endToken, const Variable * const var, const unsigned int varid, std::list values, const bool constValue, 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 ";" for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { if (indentlevel >= 0 && tok2->str() == "{") ++indentlevel; else if (indentlevel >= 0 && tok2->str() == "}") --indentlevel; 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; } } // conditional block of code that assigns variable.. else if (Token::Match(tok2, "%var% (") && Token::simpleMatch(tok2->linkAt(1), ") {")) { // Should scope be skipped because variable value is checked? bool skip = false; for (std::list::iterator it = values.begin(); it != values.end(); ++it) { if (conditionIsFalse(tok2->next()->astOperand2(), getProgramMemory(tok2, varid, *it))) { skip = true; break; } } if (skip) { // goto '{' tok2 = tok2->linkAt(1)->next(); // goto '}' tok2 = tok2->link(); 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) : (nullptr != Token::findmatch(start, "%varid%", end, varid)); if (varusage) { varusagelevel = indentlevel; if (indentlevel < 0 && tok2->str() == "switch") return false; // TODO: don't check noreturn scopes if (number_of_if > 0U || Token::findmatch(tok2, "%varid%", start, varid)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in conditional code"); return false; } if (var->isStatic()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " 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; } } // 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->nameToken()->str() + ". noreturn conditional scope."); return false; } if (isVariableChanged(start, end, varid)) { if (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 { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in conditional code"); return false; } } } 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; if (Token::simpleMatch(tok2,"} else {")) tok2 = tok2->linkAt(2); } else if (indentlevel <= 0 && Token::Match(tok2, "break|continue|goto")) { if (tok2->str() == "break") { const Scope *scope = tok2->scope(); if (scope && scope->type == Scope::eSwitch) { tok2 = const_cast(scope->classEnd); --indentlevel; continue; } } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + ". noreturn conditional scope."); return false; } else if (indentlevel <= 0 && Token::Match(tok2, "return|throw")) returnStatement = true; else if (returnStatement && tok2->str() == ";") return false; if (tok2->varId() == varid) { // bailout: assignment if (Token::Match(tok2->previous(), "!!* %var% %op%") && tok2->next()->isAssignmentOp()) { // simplify rhs for (Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->next()) { if (tok3->varId() == varid) { std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) setTokenValue(tok2, *it); } else if (Token::Match(tok3, "++|--|?|:|;")) break; } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str()); return false; } // bailout increment/decrement for now.. if (Token::Match(tok2->previous(), "++|-- %var%") || Token::Match(tok2, "%var% ++|--")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "increment/decrement of " + tok2->str()); return false; } // bailout: possible assignment using >> if (Token::Match(tok2->previous(), ">> %var% >>|;")) { const Token *parent = tok2->previous(); while (Token::simpleMatch(parent,">>")) parent = parent->astParent(); 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"); continue; } { std::list::const_iterator it; for (it = values.begin(); it != values.end(); ++it) setTokenValue(tok2, *it); } // assigned by subfunction? bool inconclusive = false; if (bailoutFunctionPar(tok2, ValueFlow::Value(), 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->inconclusive = true; } } } return true; } static void valueFlowAfterAssign(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { // Assignment if ((tok->str() != "=") || (tok->astParent())) continue; // Lhs should be a variable if (!tok->astOperand1() || !tok->astOperand1()->isName()) continue; const unsigned int varid = tok->astOperand1()->varId(); if (varid == 0U) continue; const Variable *var = tok->astOperand1()->variable(); if (!var || !var->isLocal()) 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; const bool constValue = tok->astOperand2()->isNumber(); valueFlowForward(tok, endOfVarScope, var, varid, values, constValue, tokenlist, errorLogger, settings); } } static void valueFlowAfterCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { const Token *vartok, *numtok; // Comparison if (Token::Match(tok,"==|!=|>=|<=")) { if (!tok->astOperand1() || !tok->astOperand2()) continue; if (tok->astOperand1()->isName()) { vartok = tok->astOperand1(); numtok = tok->astOperand2(); } else { vartok = tok->astOperand2(); numtok = tok->astOperand1(); } if (!vartok->isName() || !numtok->isNumber() || !MathLib::isInt(numtok->str())) 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->isArgument())) continue; std::list values; values.push_back(ValueFlow::Value(tok, numtok ? MathLib::toLongNumber(numtok->str()) : 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, "%var%|!=")))) { bool assign = false; for (; !assign && parent && parent->str() == op; parent = const_cast(parent->astParent())) { std::stack tokens; tokens.push(const_cast(parent->astOperand2())); 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()); if (Token::Match(rhstok, "++|--|=") && Token::Match(rhstok->astOperand1(),"%varid%",varid)) { assign = true; 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 (isVariableChanged(top,top->link(),varid)) { 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, "%var%|!=")) 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); } bool ok = true; if (startToken) ok = valueFlowForward(startToken->next(), startToken->link(), var, varid, values, true, tokenlist, errorLogger, settings); // After conditional code.. if (ok && 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; } 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; } } valueFlowForward(after->next(), top->scope()->classEnd, var, varid, values, true, tokenlist, errorLogger, settings); } } } } static void execute(const Token *expr, std::map * const programMemory, MathLib::bigint *result, bool *error) { if (!expr) *error = true; else if (expr->isNumber()) { *result = MathLib::toLongNumber(expr->str()); if (MathLib::isFloat(expr->str())) *error = true; } else if (expr->varId() > 0) { const std::map::const_iterator var = programMemory->find(expr->varId()); if (var == programMemory->end()) *error = true; else *result = var->second; } 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)[expr->astOperand1()->varId()] = *result; else *error = true; } else if (expr->str() == "++" || expr->str() == "--") { if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U) *error = true; else { std::map::iterator var = programMemory->find(expr->astOperand1()->varId()); if (var == programMemory->end()) *error = true; else { if (var->second == 0 && expr->str() == "--" && expr->astOperand1()->variable() && expr->astOperand1()->variable()->typeStartToken()->isUnsigned()) *error = true; // overflow *result = var->second + (expr->str() == "++" ? 1 : -1); var->second = *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() == "*") *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() == "&&") { 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 *error = true; } static bool valueFlowForLoop1(const Token *tok, unsigned int * const varid, MathLib::bigint * const num1, MathLib::bigint * const num2) { tok = tok->tokAt(2); if (!Token::Match(tok,"%type%| %var% =")) return false; const Token * const vartok = tok->tokAt(Token::Match(tok, "%var% =") ? 0 : 1); if (vartok->varId() == 0U) return false; *varid = vartok->varId(); const Token * const num1tok = Token::Match(vartok->tokAt(2), "%num% ;") ? vartok->tokAt(2) : nullptr; if (num1tok) *num1 = MathLib::toLongNumber(num1tok->str()); tok = vartok->tokAt(2); while (Token::Match(tok, "%var%|%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%")) num2tok = 0; } if (!num2tok) return false; *num2 = MathLib::toLongNumber(num2tok->str()) - ((tok->str()=="<=") ? 0 : 1); if (!num1tok) *num1 = *num2; while (tok && tok->str() != ";") tok = tok->next(); if (!Token::Match(tok, "; %varid% ++ ) {", vartok->varId())) return false; return true; } static bool valueFlowForLoop2(const Token *tok, std::map *memory1, std::map *memory2) { const Token *firstExpression = tok->next()->astOperand2()->astOperand1(); const Token *secondExpression = tok->next()->astOperand2()->astOperand2()->astOperand1(); const Token *thirdExpression = tok->next()->astOperand2()->astOperand2()->astOperand2(); std::map programMemory; MathLib::bigint result(0); bool error = false; execute(firstExpression, &programMemory, &result, &error); if (error) return false; execute(secondExpression, &programMemory, &result, &error); 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() == "=" && programMemory.find(t->astOperand1()->varId()) != programMemory.end()) // TODO: investigate what variable is assigned. return false; tokens.push(t->astOperand1()); tokens.push(t->astOperand2()); } } std::map startMemory(programMemory); std::map 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); return true; } static void valueFlowForLoopSimplify(Token * const bodyStart, const unsigned int varid, 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)) 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 && parent->str() == "?") { if (parent->astOperand2() != p) parent = NULL; 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); } if (Token::Match(tok2, "%oror%|&&")) { const std::map programMemory(getProgramMemory(tok2->astTop(), varid, value)); if ((tok2->str() == "&&" && conditionIsFalse(tok2->astOperand1(), programMemory)) || (tok2->str() == "||" && conditionIsTrue(tok2->astOperand1(), programMemory))) { // Skip second expression.. const Token *parent = tok2; while (parent && parent->str() == tok2->str()) parent = parent->astParent(); if (parent && parent->str() == "(") tok2 = parent->link(); } } if ((tok2->str() == "&&" && conditionIsFalse(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, value))) || (tok2->str() == "||" && conditionIsTrue(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, 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); } } } } static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "for (") || !Token::simpleMatch(tok->next()->astOperand2(), ";") || !Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) continue; Token * const bodyStart = tok->linkAt(1)->next(); unsigned int varid(0); MathLib::bigint num1(0), num2(0); if (valueFlowForLoop1(tok, &varid, &num1, &num2)) { valueFlowForLoopSimplify(bodyStart, varid, num1, tokenlist, errorLogger, settings); valueFlowForLoopSimplify(bodyStart, varid, num2, tokenlist, errorLogger, settings); } else { std::map mem1, mem2; if (valueFlowForLoop2(tok, &mem1, &mem2)) { std::map::const_iterator it; for (it = mem1.begin(); it != mem1.end(); ++it) valueFlowForLoopSimplify(bodyStart, it->first, it->second, tokenlist, errorLogger, settings); for (it = mem2.begin(); it != mem2.end(); ++it) valueFlowForLoopSimplify(bodyStart, it->first, it->second, tokenlist, errorLogger, settings); } } } } static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { std::list argvalues; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!Token::Match(tok, "[(,]")) continue; // passing value(s) to function if (Token::Match(tok, "[(,] %var% [,)]") && !tok->next()->values.empty()) argvalues = tok->next()->values; else if (Token::Match(tok, "[(,] %num% [,)]") && MathLib::isInt(tok->strAt(1))) { argvalues.clear(); argvalues.push_back(ValueFlow::Value(MathLib::toLongNumber(tok->next()->str()))); } else { // bool operator => values 1/0 are passed to function.. const Token *op = tok->next(); while (op && op->astParent() && !Token::Match(op->astParent(), "[(,]")) op = op->astParent(); if (Token::Match(op, "%comp%|%oror%|&&|!")) { argvalues.clear(); argvalues.push_back(ValueFlow::Value(0)); argvalues.push_back(ValueFlow::Value(1)); } else { // possible values are unknown.. continue; } } const Token * const argumentToken = tok->next(); // is this a function call? const Token *ftok = tok; while (ftok && ftok->str() != "(") ftok = ftok->astParent(); if (!ftok || !ftok->astOperand1() || !ftok->astOperand2() || !ftok->astOperand1()->function()) continue; // Get argument nr unsigned int argnr = 0; for (const Token *argtok = ftok->next(); argtok && argtok != argumentToken; argtok = argtok->nextArgument()) ++ argnr; // Get function argument, and check if parameter is passed by value const Function * const function = ftok->astOperand1()->function(); const Variable * const arg = function ? function->getArgumentVar(argnr) : nullptr; if (!Token::Match(arg ? arg->typeStartToken() : nullptr, "%type% %var% ,|)")) continue; // Function scope.. const Scope * const functionScope = function ? function->functionScope : nullptr; if (!functionScope) continue; // Set value in function scope.. const unsigned int varid2 = arg->nameToken()->varId(); for (const Token *tok2 = functionScope->classStart->next(); tok2 != functionScope->classEnd; tok2 = tok2->next()) { if (Token::Match(tok2, "%varid% !!=", varid2)) { for (std::list::const_iterator val = argvalues.begin(); val != argvalues.end(); ++val) setTokenValue(const_cast(tok2), *val); } else if (tok2->str() == "{" || tok2->str() == "?") { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "parameter " + arg->nameToken()->str() + ", at '" + tok2->str() + "'"); break; } } } } static bool constval(const Token * tok) { return tok && tok->values.size() == 1U && tok->values.front().varId == 0U; } static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand1()->function()) continue; // Arguments.. std::vector parvalues; { const Token *partok = tok->astOperand2(); while (partok && partok->str() == "," && constval(partok->astOperand2())) partok = partok->astOperand1(); if (!constval(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 ? function->functionScope : nullptr; if (!functionScope || !Token::simpleMatch(functionScope->classStart, "{ return")) { if (functionScope && settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "function return; nontrivial function body"); continue; } std::map 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% %var% ,|)")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "function return; unhandled argument type"); programMemory.clear(); break; } programMemory[arg->declarationId()] = parvalues[i]; } if (programMemory.empty()) continue; // Determine return value of subfunction.. MathLib::bigint result = 0; bool error = false; execute(functionScope->classStart->next()->astOperand1(), &programMemory, &result, &error); if (!error) setTokenValue(tok, ValueFlow::Value(result)); } } void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) tok->values.clear(); valueFlowNumber(tokenlist); valueFlowFunctionReturn(tokenlist, errorLogger, settings); valueFlowBitAnd(tokenlist); valueFlowForLoop(tokenlist, errorLogger, settings); valueFlowBeforeCondition(tokenlist, errorLogger, settings); valueFlowAfterAssign(tokenlist, errorLogger, settings); valueFlowAfterCondition(tokenlist, errorLogger, settings); valueFlowSubFunction(tokenlist, errorLogger, settings); } cppcheck-1.66/lib/valueflow.h000066400000000000000000000037421236713773000161700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 //--------------------------------------------------------------------------- class Token; class TokenList; class ErrorLogger; class Settings; namespace ValueFlow { class Value { public: Value(long long val = 0) : intvalue(val), varvalue(val), condition(0), varId(0U), conditional(false), inconclusive(false) {} Value(const Token *c, long long val) : intvalue(val), varvalue(val), condition(c), varId(0U), conditional(false), inconclusive(false) {} /** int value */ long long intvalue; /** For calculated values - variable value that calculated value depends on */ long long varvalue; /** Condition that this value depends on (TODO: replace with a 'callstack') */ const Token *condition; /** For calculated values - varId that calculated value depends on */ unsigned int varId; /** Conditional value */ bool conditional; /** Is this value inconclusive? */ bool inconclusive; }; void setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); } #endif // valueflowH cppcheck-1.66/lib/version.h000066400000000000000000000002441236713773000156430ustar00rootroot00000000000000#define CPPCHECK_VERSION_STRING "1.66" #define CPPCHECK_VERSION 1,66,0,0 #define LEGALCOPYRIGHT L"Copyright (C) 2007-2014 Daniel Marjam\x00E4ki and Cppcheck team." cppcheck-1.66/lib/version.rc000066400000000000000000000016371236713773000160270ustar00rootroot00000000000000#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.66/man/000077500000000000000000000000001236713773000140125ustar00rootroot00000000000000cppcheck-1.66/man/buildman.sh000077500000000000000000000002101236713773000161350ustar00rootroot00000000000000#/bin/sh xsltproc -o manual.html /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl manual.docbook docbook2pdf manual.docbook cppcheck-1.66/man/cppcheck-design.docbook000066400000000000000000000130351236713773000204050ustar00rootroot00000000000000 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.66/man/cppcheck.1.xml000066400000000000000000000605411236713773000164610ustar00rootroot00000000000000 .
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 - 2014 &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. This allows you to provide information about functions by providing an implementation for these. 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.66/man/manual.docbook000066400000000000000000001066301236713773000166370ustar00rootroot00000000000000 Cppcheck 1.66 2013-12-23 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. Getting started
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 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
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
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 Informational messages about checking problems.
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 information messages cppcheck --enable=warning,information 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. And 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
Preprocessor configurations By default Cppcheck will check all preprocessor configurations (except those that have #error in them). 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. XML output Cppcheck can generate the output in XML format. There is an old XML format (version 1) and a new XML format (version 2). Please use the new version if you can. The old version is kept for backwards compatibility only. It will not be changed. But it will likely be removed someday. Use --xml to enable this format. The new version fixes a few problems with the old format. The new format will probably be updated in future versions of cppcheck with new attributes and elements. A sample command to check a file and output errors in the new XML format: cppcheck --xml-version=2 file1.cppHere is a sample version 2 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"> <location file="file.c" 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.
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 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 severity 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 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 windows/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. If you create 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. 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 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 windows 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 windows 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>
Function argument: 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 windows configuration file then Cppcheck detects the bug: # cppcheck --library=windows.cfg uninit.c Checking uninit.c... [uninit.c:5]: (error) Uninitialized variable: buffer2 Here is the minimal windows.cfg: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="2"> <not-uninit/> </arg> </function> </def>
Function Argument: 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 windows configuration file then Cppcheck detects the bug: cppcheck --library=windows.cfg null.c Checking null.c... [null.c:3]: (error) Null pointer dereference Here is a minimal windows.cfg file: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"> <not-null/> </arg> </function> </def>
Function Argument: 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"> <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.
Function Argument: 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'.
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> </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/> </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.
Specifications for all arguments Specifying -1 as the argument number is going to apply a check to all arguments of that function. The specifications for individual arguments override this setting.
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>
<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 extensions with Python Using dump files it is possible to write Cppcheck extensions with for instance Python. The cppcheckdata.py module (http://github.com/danmar/cppcheck/blob/master/tools/cppcheckdata.py) allows you to load such dump file. It contains Token/Variable/ValueFlow.Value/Scope classes that are similar to the C++ classes in Cppcheck-core. The doxygen information for the C++ classes should be somewhat useful for Python developers also.
Simple checker: Division by zero Here is a simple checker: import cppcheckdata data = cppcheckdata.parsedump('1.c.dump') for token in data.tokenlist: if token.str == '/' or token.str == '%': # Get denominator (2nd operand) den = token.astOperand2 # Can denominator be zero? if den.getValue(0): print '[' + token.file + ':' + str(token.linenr) + '] Division by zero' Example usage: cppcheck --dump 1.c python divzero.py
Licensing The dump file is just a xml file, so it is an open interface without restrictions. You can use it in any way you need. The cppcheckdata.py is also free to use. No matter if your project is open source or closed source. Use it for any purpose.
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.66/man/writing-rules-1.docbook000066400000000000000000000106071236713773000203310ustar00rootroot00000000000000
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.66/man/writing-rules-2.docbook000066400000000000000000000201041236713773000203230ustar00rootroot00000000000000
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.66/man/writing-rules-3.docbook000066400000000000000000000164751236713773000203440ustar00rootroot00000000000000
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.66/readme.md000066400000000000000000000062461236713773000150260ustar00rootroot00000000000000# Cppcheck [![Build Status](https://travis-ci.org/danmar/cppcheck.png?branch=master)](https://travis-ci.org/danmar/cppcheck) [![Coverity Scan Build Status](https://scan.coverity.com/projects/512/badge.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 * Windows: Visual Studio (VS 2010 and above) or Qt Creator or MinGW * gnu make * g++ 4.4 (and above) * clang++ 2.9 (and above) ### 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 rules are normally enabled. To compile with rules (PCRE dependency): * the PCRE dll is needed. It can be downloaded from [here](http://cppcheck.sourceforge.net/pcre-8.10-vs.zip). To compile without rules (no dependencies): * remove the preprocessor define `HAVE_RULES` from the project * remove the pcre.lib from the project ### 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): ```shell make ``` The recommended release build is: ```shell make SRCDIR=build CFGDIR=cfg HAVE_RULES=yes ``` 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) ### 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/tinyxml -Ilib cli/*.cpp lib/*.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/tinyxml cli/*.cpp lib/*.cpp externals/tinyxml/*.cpp ``` ### MinGW ```shell 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. Compile. ### Cross compiling Win32 (CLI) version of Cppcheck in Linux ```shell sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" mv cppcheck cppcheck.exe ``` ## Webpage http://cppcheck.sourceforge.net/ cppcheck-1.66/readme.txt000066400000000000000000000060511236713773000152370ustar00rootroot00000000000000========= 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 * Windows: Visual Studio * Windows: Qt Creator + mingw * gnu make * g++ 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 rules are normally enabled. To compile with rules (pcre dependency): * the pcre dll is needed. it can be downloaded from: http://cppcheck.sourceforge.net/pcre-8.10-vs.zip To compile without rules (no dependencies): * remove the preprocessor define HAVE_RULES from the project * remove the pcre.lib from the project 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/tinyxml -Ilib cli/*.cpp lib/*.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/tinyxml cli/*.cpp lib/*.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. 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.66/readme_64-bit_Windows.txt000066400000000000000000000016751236713773000200450ustar00rootroot00000000000000Cppcheck for 64-bit Windows =========================== This is quick start to get you started with compiling Cppcheck for 64-bit Windows with free VS Express editions. Software needed: - Visual Studio 2010 (or later) Express edition - Only for VS2010: Windows SDK 7.1 LIB, CLI and testsuite ---------------------- Visual Studio 2010 and later: Just open cppcheck.sln, choose "x64" as platform and compile. GUI --- Software needed: - Visual Studio 2010 Express edition - Windows SDK 7.1 (containing x64 compiler) - latest Qt SDK (4.7.0 or later, earlier versions don't support VS 2010) Turns out you just need to use Windows SDK's Command prompt and 64-bit environment to configure and build Qt. No extra steps needed. But of course you should build 64-bit Qt to different folder than 32-bit Qt. Compiling 64-bit GUI works fine from Windows SDK Console. With VS2010 Express IDE everything works fine after adding new x64 platform for the project. cppcheck-1.66/readme_gui.txt000066400000000000000000000054121236713773000161030ustar00rootroot00000000000000Cppcheck 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 Qt4 libraries installed in your system. Packages/files to install depends on your operating system: - Windows: download Qt4 from http://qt-project.org/downloads - Linux: install Qt4 using your package manager, look for packages having Qt4 in their name, e.g. for Ubuntu install libqt4-core and libqt4-gui. Compiling --------- Windows: - The easy ways are: -- download Qt SDK from http://qt-project.org/downloads 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: * libqt4-core * libqt4-gui * libqt4-dev * qt4-dev-tools * qt4-qmake 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. 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.66/rules/000077500000000000000000000000001236713773000143715ustar00rootroot00000000000000cppcheck-1.66/rules/error-reporting.xml000066400000000000000000000004631236713773000202560ustar00rootroot00000000000000 Severity :: fromString \( "\w+" \) ConstantSeverityFromString style Constant severity lookups should be done via Severity::constant. cppcheck-1.66/rules/show-all-defines.rule000066400000000000000000000003511236713773000204220ustar00rootroot00000000000000 define .* showalldefines information cppcheck-1.66/rules/stl.xml000066400000000000000000000005541236713773000157210ustar00rootroot00000000000000 \. find \( "[^"]+?" \) == \d+ UselessSTDStringFind performance When looking for a string at a fixed position compare is faster. cppcheck-1.66/rules/strlen-empty-str.xml000066400000000000000000000005001236713773000203570ustar00rootroot00000000000000 if \( ([!] )*?(strlen) \( \w+? \) ([>] [0] )*?\) { StrlenEmptyString performance Using strlen() to check if a string is empty is not efficient. cppcheck-1.66/rules/token-matching.xml000066400000000000000000000020421236713773000200210ustar00rootroot00000000000000 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.66/rules/unused-deref.xml000066400000000000000000000004221236713773000174770ustar00rootroot00000000000000 [;{}] [*] \w+? (\+\+|\-\-) ; UnusedDeref style Redundant * found, "*p++" is the same as "*(p++)". cppcheck-1.66/runastyle000077500000000000000000000025711236713773000152200ustar00rootroot00000000000000#!/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 2.03" if [[ "`astyle --version 2>&1`" != ${ASTYLE_VERSION}* ]]; then echo "You should use: ${ASTYLE_VERSION}"; exit 1; fi style="--style=stroustrup --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0" options="--options=none --pad-header --unpad-paren --suffix=none --convert-tabs" 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/*.h astyle $style $options tools/*.cpp 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 # strip useless whitespaces from config files # sed -i "s/\ $//" cfg/* cppcheck-1.66/runastyle.bat000066400000000000000000000013301236713773000157520ustar00rootroot00000000000000REM A script to run Astyle for the sources SET STYLE=--style=stroustrup --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0 SET OPTIONS=--pad-header --unpad-paren --suffix=none --convert-tabs astyle %STYLE% %OPTIONS% cli/*.cpp astyle %STYLE% %OPTIONS% cli/*.h 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/*.h astyle %STYLE% %OPTIONS% tools/*.cpp astyle %STYLE% %OPTIONS% htdocs/archive/*.c astyle %STYLE% %OPTIONS% htdocs/archive/*.h cppcheck-1.66/samples/000077500000000000000000000000001236713773000147035ustar00rootroot00000000000000cppcheck-1.66/samples/AssignmentAddressToInteger/000077500000000000000000000000001236713773000221425ustar00rootroot00000000000000cppcheck-1.66/samples/AssignmentAddressToInteger/bad.c000066400000000000000000000001401236713773000230270ustar00rootroot00000000000000int foo(int *p) { int a = p; return a + 4; } int main() { int i[10]; foo(i); } cppcheck-1.66/samples/AssignmentAddressToInteger/good.c000066400000000000000000000001221236713773000232310ustar00rootroot00000000000000int* foo(int *p) { return p + 4; } int main() { int i[10]; foo(i); } cppcheck-1.66/samples/AssignmentAddressToInteger/out.txt000066400000000000000000000001571236713773000235150ustar00rootroot00000000000000[samples\AssignmentAddressToInteger\bad.c:3]: (portability) Assigning a pointer to an integer is not portable. cppcheck-1.66/samples/arrayIndexOutOfBounds/000077500000000000000000000000001236713773000211415ustar00rootroot00000000000000cppcheck-1.66/samples/arrayIndexOutOfBounds/bad.c000066400000000000000000000001301236713773000220250ustar00rootroot00000000000000int main() { int a[2]; a[0] = 0; a[1] = 0; a[2] = 0; return a[0]; } cppcheck-1.66/samples/arrayIndexOutOfBounds/good.c000066400000000000000000000001301236713773000222270ustar00rootroot00000000000000int main() { int a[3]; a[0] = 0; a[1] = 0; a[2] = 0; return a[0]; } cppcheck-1.66/samples/arrayIndexOutOfBounds/out.txt000066400000000000000000000001531236713773000225100ustar00rootroot00000000000000[samples\arrayIndexOutOfBounds\bad.c:6]: (error) Array 'a[2]' accessed at index 2, which is out of bounds. cppcheck-1.66/samples/autoVariables/000077500000000000000000000000001236713773000175045ustar00rootroot00000000000000cppcheck-1.66/samples/autoVariables/bad.c000066400000000000000000000001331236713773000203730ustar00rootroot00000000000000void foo(int **a) { int b = 1; *a = &b; } int main() { int *c; foo(&c); } cppcheck-1.66/samples/autoVariables/good.c000066400000000000000000000001531236713773000205770ustar00rootroot00000000000000void foo(int **a) { int b = 1; **a = b; } int main() { int b; int *c = &b; foo(&c); } cppcheck-1.66/samples/autoVariables/out.txt000066400000000000000000000001521236713773000210520ustar00rootroot00000000000000[samples\autoVariables\bad.c:4]: (error) Address of local auto-variable assigned to a function parameter. cppcheck-1.66/samples/bufferAccessOutOfBounds/000077500000000000000000000000001236713773000214265ustar00rootroot00000000000000cppcheck-1.66/samples/bufferAccessOutOfBounds/bad.c000066400000000000000000000001471236713773000223220ustar00rootroot00000000000000int main() { int a[2]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-1.66/samples/bufferAccessOutOfBounds/good.c000066400000000000000000000001471236713773000225240ustar00rootroot00000000000000int main() { int a[3]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-1.66/samples/bufferAccessOutOfBounds/out.txt000066400000000000000000000001551236713773000227770ustar00rootroot00000000000000[samples\bufferAccessOutOfBounds\bad.c:6]: (error) Array 'a[2]' accessed at index 2, which is out of bounds. cppcheck-1.66/samples/erase/000077500000000000000000000000001236713773000160025ustar00rootroot00000000000000cppcheck-1.66/samples/erase/bad.cpp000066400000000000000000000004531236713773000172360ustar00rootroot00000000000000#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 (true) { items.erase(iter); } } } cppcheck-1.66/samples/erase/good.cpp000066400000000000000000000005201236713773000174330ustar00rootroot00000000000000#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 (true) { iter = items.erase(iter); } else { ++iter; } } } cppcheck-1.66/samples/erase/out.txt000066400000000000000000000001651236713773000173540ustar00rootroot00000000000000[samples\erase\bad.cpp:9] -> [samples\erase\bad.cpp:11]: (error) Iterator 'iter' used after element has been erased. cppcheck-1.66/samples/memleak/000077500000000000000000000000001236713773000163165ustar00rootroot00000000000000cppcheck-1.66/samples/memleak/bad.c000066400000000000000000000002011236713773000172010ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; return result; } cppcheck-1.66/samples/memleak/good.c000066400000000000000000000002161236713773000174110ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; free(a); return result; } cppcheck-1.66/samples/memleak/out.txt000066400000000000000000000000621236713773000176640ustar00rootroot00000000000000[samples\memleak\bad.c:8]: (error) Memory leak: a cppcheck-1.66/samples/outOfBounds/000077500000000000000000000000001236713773000171525ustar00rootroot00000000000000cppcheck-1.66/samples/outOfBounds/bad.c000066400000000000000000000001271236713773000200440ustar00rootroot00000000000000#include int main() { char str[5]; snprintf(str, 10, "%s", "abc"); } cppcheck-1.66/samples/outOfBounds/good.c000066400000000000000000000001301236713773000202400ustar00rootroot00000000000000#include int main() { char str[10]; snprintf(str, 10, "%s", "abc"); } cppcheck-1.66/samples/outOfBounds/out.txt000066400000000000000000000001661236713773000205250ustar00rootroot00000000000000[samples\outOfBounds\bad.c:5]: (error) snprintf size is out of bounds: Supplied size 10 is larger than actual size 5. cppcheck-1.66/samples/resourceLeak/000077500000000000000000000000001236713773000173275ustar00rootroot00000000000000cppcheck-1.66/samples/resourceLeak/bad.c000066400000000000000000000001631236713773000202210ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; return 0; } cppcheck-1.66/samples/resourceLeak/good.c000066400000000000000000000002011236713773000204140ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; fclose(a); return 0; } cppcheck-1.66/samples/resourceLeak/out.txt000066400000000000000000000000711236713773000206750ustar00rootroot00000000000000[samples\resourceLeak\bad.c:8]: (error) Resource leak: a cppcheck-1.66/samples/syntaxError/000077500000000000000000000000001236713773000172435ustar00rootroot00000000000000cppcheck-1.66/samples/syntaxError/bad.c000066400000000000000000000000401236713773000201270ustar00rootroot00000000000000int main() { #ifndef A } #endif cppcheck-1.66/samples/syntaxError/good.c000066400000000000000000000000401236713773000203310ustar00rootroot00000000000000int main() { #ifndef A #endif } cppcheck-1.66/samples/syntaxError/out.txt000066400000000000000000000001531236713773000206120ustar00rootroot00000000000000[samples\syntaxError\bad.c:2]: (error) Invalid number of character ({) when these macros are defined: 'A'. cppcheck-1.66/test/000077500000000000000000000000001236713773000142165ustar00rootroot00000000000000cppcheck-1.66/test/options.cpp000066400000000000000000000024521236713773000164200ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "options.h" options::options(int argc, const char* argv[]) :_options(&argv[1], &argv[0] + argc) ,_which_test("") ,_gcc_style_errors(_options.count("-g") != 0) ,_quiet(_options.count("-q") != 0) { _options.erase("-g"); _options.erase("-q"); if (! _options.empty()) { _which_test = *_options.rbegin(); } } bool options::quiet() const { return _quiet; } bool options::gcc_style_errors() const { return _gcc_style_errors; } const std::string& options::which_test() const { return _which_test; } cppcheck-1.66/test/options.h000066400000000000000000000032751236713773000160710ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public 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; /** __FILE__:__LINE__: Error message. Makes it easier for editors to find * failing tests/ */ bool gcc_style_errors() 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 _gcc_style_errors; const bool _quiet; }; #endif cppcheck-1.66/test/redirect.h000066400000000000000000000053501236713773000161730ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public 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 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.66/test/test.cxx000066400000000000000000000007271236713773000157270ustar00rootroot00000000000000/* 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.66/test/test.pro000066400000000000000000000013031236713773000157140ustar00rootroot00000000000000TEMPLATE = app TARGET = testrunner DEPENDPATH += . INCLUDEPATH += . ../cli ../lib OBJECTS_DIR = temp CONFIG += warn_on console CONFIG -= qt app_bundle include(../console_common.pri) BASEPATH = ../lib/ include(../lib/lib.pri) BASEPATH = . include($$PWD/testfiles.pri) # cli/* SOURCES += ../cli/cmdlineparser.cpp \ ../cli/cppcheckexecutor.cpp \ ../cli/filelister.cpp \ ../cli/pathmatch.cpp \ ../cli/threadexecutor.cpp HEADERS += ../cli/cmdlineparser.h \ ../cli/cppcheckexecutor.h \ ../cli/filelister.h \ ../cli/pathmatch.h \ ../cli/threadexecutor.h HEADERS += options.h redirect.h testsuite.h SOURCES += options.cpp cppcheck-1.66/test/test64bit.cpp000066400000000000000000000145321236713773000165570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "check64bit.h" #include "testsuite.h" #include extern std::ostringstream errout; class Test64BitPortability : public TestFixture { public: Test64BitPortability() : TestFixture("Test64BitPortability") { } private: void run() { 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(""); Settings settings; settings.addEnabled("portability"); // 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()); } void structmember() { check("struct Foo { int *p };\n" "void f(struct Foo *foo) {\n" " int i = foo->p;\n" "}"); TODO_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" "}"); TODO_ASSERT_EQUALS("error", "", 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("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()); } }; REGISTER_TEST(Test64BitPortability) cppcheck-1.66/test/testassert.cpp000066400000000000000000000146551236713773000171360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkassert.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestAssert : public TestFixture { public: TestAssert() : TestFixture("TestAsserts") {} private: void check( const char code[], const char *filename = NULL) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); settings.addEnabled("warning"); settings.addEnabled("portability"); settings.addEnabled("performance"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename ? filename : "test.cpp"); // Check.. CheckAssert checkAssert(&tokenizer, &settings, this); checkAssert.runSimplifiedChecks(&tokenizer, &settings, this); } void run() { TEST_CASE(assignmentInAssert); TEST_CASE(functionCallInAssert); 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"); TODO_ASSERT_EQUALS("", "[test.cpp:8]: (warning) Assert statement calls a function which may have desired side effects: 'isRank1Or8'.\n", 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()); } }; REGISTER_TEST(TestAssert) cppcheck-1.66/test/testassignif.cpp000066400000000000000000000353621236713773000174360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkassignif.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestAssignIf : public TestFixture { public: TestAssignIf() : TestFixture("TestAssignIf") { } private: void run() { TEST_CASE(assignAndCompare); // assignment and comparison don't match TEST_CASE(mismatchingBitAnd); // overlapping bitmasks TEST_CASE(compare); // mismatching LHS/RHS in comparison TEST_CASE(multicompare); // mismatching comparisons TEST_CASE(duplicateIf); // duplicate conditions in if and else-if TEST_CASE(invalidMissingSemicolon); // crash as of #5867 } void check(const char code[], bool validate=true) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); CheckAssignIf checkAssignIf; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); checkAssignIf.runChecks(&tokenizer, &settings, this); const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); checkAssignIf.runSimplifiedChecks(&tokenizer, &settings, this); // Ensure that the test case is not bad. if (validate && str1 != str2) { warn(("Unsimplified code in test case. It looks like this test " "should either be cleaned up or moved to TestTokenizer or " "TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str()); } } 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()); // 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()); } 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 compare() { check("void foo(int x)\n" "{\n" " if ((x & 4) == 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str()); check("void foo(int x)\n" "{\n" " if ((x | 4) == 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X | 0x4) == 0x3' is always false.\n", errout.str()); check("void foo(int x)\n" "{\n" " if ((x | 4) != 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X | 0x4) != 0x3' is always true.\n", errout.str()); check("void foo(int x)\n" "{\n" " if ((x & y & 4 & z ) == 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str()); } 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()); } 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" "}",false); ASSERT_EQUALS("", errout.str()); } void invalidMissingSemicolon() { // simply survive - a syntax error would be even better check("void f(int x) {\n" " x = 42\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestAssignIf) cppcheck-1.66/test/testautovariables.cpp000066400000000000000000000766351236713773000205040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkautovariables.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestAutoVariables : public TestFixture { public: TestAutoVariables() : TestFixture("TestAutoVariables") { } private: void check(const char code[], bool inconclusive=false, bool runSimpleChecks=true, const char* filename=nullptr) { // Clear the error buffer.. errout.str(""); Settings settings; settings.inconclusive = inconclusive; settings.addEnabled("warning"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, (filename)?filename:"test.cpp"); CheckAutoVariables checkAutoVariables(&tokenizer, &settings, this); checkAutoVariables.returnReference(); checkAutoVariables.assignFunctionArg(); if (runSimpleChecks) { const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); if (str1 != str2) warn(("Unsimplified code in test case. It looks like this test " "should either be cleaned up or moved to TestTokenizer or " "TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str()); // Check auto variables checkAutoVariables.autoVariables(); checkAutoVariables.returnPointerToLocalArray(); } } void run() { 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(testautovar_array1); TEST_CASE(testautovar_array2); TEST_CASE(testautovar_return1); TEST_CASE(testautovar_return2); TEST_CASE(testautovar_return3); TEST_CASE(testautovar_return4); // ticket #3030 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); // 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); // 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.\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.\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.\n", errout.str()); check("void foo(int b) {\n" " b = foo(b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function.\n", 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.\n", errout.str()); check("class Foo {};\n" "void foo(Foo p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) 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()); } 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 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_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_return4() { // #3030 check("char *foo()\n" "{\n" " char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of an auto-variable returned.\n", errout.str()); check("char *foo()\n" "{\n" " static char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_extern() { check("struct foo *f()\n" "{\n" " extern struct foo f;\n" " return &f;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc() { check("void func1() {\n" " char tmp1[256];\n" " free(tmp1);\n" " char tmp2[256];\n" " delete tmp2;\n" " char tmp3[256];\n" " delete tmp3;\n" " char tmp4[256];\n" " delete[] (tmp4);\n" " char tmp5[256];\n" " delete[] tmp5;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:5]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:7]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:9]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:11]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("void func1() {\n" " char* tmp1[256];\n" " init(tmp1);\n" " delete tmp1[34];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void 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" "}"); TODO_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" "}"); TODO_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()); } 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("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()); } 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 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("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()); } 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.66/test/testbool.cpp000066400000000000000000001021361236713773000165600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkbool.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestBool : public TestFixture { public: TestBool() : TestFixture("TestBool") { } private: void run() { 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 settings; settings.addEnabled("style"); settings.addEnabled("warning"); settings.inconclusive = true; 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()); } 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()); } 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()); } 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]: (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 with an integer.\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 with an integer.\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 with an integer that is neither 1 nor 0.\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 with an integer that is neither 1 nor 0.\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 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 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 with an integer.\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 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 with an integer.\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" " 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()); } }; REGISTER_TEST(TestBool) cppcheck-1.66/test/testboost.cpp000066400000000000000000000071511236713773000167540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkboost.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestBoost : public TestFixture { public: TestBoost() : TestFixture("TestBoost") { } private: void run() { TEST_CASE(BoostForeachContainerModification) } void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); settings.addEnabled("performance"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); if (str1 != str2) warn("Unsimplified code in test case"); // 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()); } }; REGISTER_TEST(TestBoost) cppcheck-1.66/test/testbufferoverrun.cpp000066400000000000000000004416311236713773000205250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkbufferoverrun.h" #include "testsuite.h" #include #include extern std::ostringstream errout; class TestBufferOverrun : public TestFixture { public: TestBufferOverrun() : TestFixture("TestBufferOverrun") { } private: void check(const char code[], bool experimental = true, const char filename[] = "test.cpp", bool verify = true) { // Clear the error buffer.. errout.str(""); Settings settings; settings.inconclusive = true; settings.standards.posix = true; settings.experimental = experimental; settings.addEnabled("warning"); settings.addEnabled("style"); settings.addEnabled("portability"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); const std::string str1(tokenizer.tokens()->stringifyList(0,true)); // Assign variable ids tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); // Ensure that the test case is not bad. if (verify && str1 != str2) { warn(("Unsimplified code in test case. It looks like this test " "should either be cleaned up or moved to TestTokenizer or " "TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str()); } // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun(&tokenizer, &settings, this); checkBufferOverrun.bufferOverrun(); checkBufferOverrun.bufferOverrun2(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.writeOutsideBufferSize(); } void checkstd(const char code[], const char filename[] = "test.cpp") { static bool init; static Settings settings; if (!init) { init = true; LOAD_LIB_2(settings.library, "std.cfg"); settings.addEnabled("warning"); } 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.bufferOverrun(); checkBufferOverrun.bufferOverrun2(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.writeOutsideBufferSize(); } void checkposix(const char code[], const char filename[] = "test.cpp") { static bool init; static Settings settings; if (!init) { init = true; LOAD_LIB_2(settings.library, "posix.cfg"); settings.addEnabled("warning"); } 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.bufferOverrun(); checkBufferOverrun.bufferOverrun2(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.writeOutsideBufferSize(); } void run() { 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_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(buffer_overrun_1_standard_functions); TEST_CASE(buffer_overrun_1_posix_functions); 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_12); TEST_CASE(buffer_overrun_13); TEST_CASE(buffer_overrun_14); TEST_CASE(buffer_overrun_15); // ticket #1787 TEST_CASE(buffer_overrun_16); TEST_CASE(buffer_overrun_17); // ticket #2548 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_20); // #2986 (segmentation fault) TEST_CASE(buffer_overrun_21); TEST_CASE(buffer_overrun_22); // #3124 TEST_CASE(buffer_overrun_23); // #3153 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_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch TEST_CASE(buffer_overrun_function_array_argument); TEST_CASE(possible_buffer_overrun_1); // #3035 // 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(sprintf1); TEST_CASE(sprintf2); TEST_CASE(sprintf3); TEST_CASE(sprintf4); TEST_CASE(sprintf5); TEST_CASE(sprintf6); TEST_CASE(sprintf7); TEST_CASE(sprintf8); TEST_CASE(sprintf9); TEST_CASE(sprintf10); TEST_CASE(snprintf1); TEST_CASE(snprintf2); TEST_CASE(snprintf4); TEST_CASE(snprintf5); TEST_CASE(snprintf6); TEST_CASE(snprintf7); TEST_CASE(strncat1); TEST_CASE(strncat2); TEST_CASE(strncat3); TEST_CASE(strncat4); TEST_CASE(strcat1); TEST_CASE(strcat2); TEST_CASE(strcat3); TEST_CASE(memfunc1); // memchr/memset/memcpy TEST_CASE(memfunc2); TEST_CASE(memfunc3); // ticket #1659 TEST_CASE(varid1); TEST_CASE(varid2); TEST_CASE(varid3); // ticket #4764 TEST_CASE(assign1); TEST_CASE(alloc1); // Buffer allocated with new TEST_CASE(alloc2); // Buffer allocated with malloc TEST_CASE(alloc3); // statically allocated buffer TEST_CASE(alloc4); // Buffer allocated with alloca TEST_CASE(malloc_memset); // using memset on buffer allocated with malloc TEST_CASE(memset1); TEST_CASE(memset2); TEST_CASE(counter_test); TEST_CASE(strncpy1); 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 #2607 - crash TEST_CASE(crash3); // Ticket #3034 - crash TEST_CASE(crash4); // Ticket #5426 - crash TEST_CASE(crash5); // TIcket #5595 - crash TEST_CASE(garbage1); // Ticket #5203 TEST_CASE(executionPaths1); TEST_CASE(executionPaths2); TEST_CASE(executionPaths3); // no FP for function parameter TEST_CASE(executionPaths4); // Ticket #2386 - Segmentation fault in the ExecutionPath handling TEST_CASE(executionPaths5); // Ticket #2920 - False positive when size is unknown TEST_CASE(executionPaths6); // unknown types TEST_CASE(cmdLineArgs1); TEST_CASE(scope); // handling different scopes TEST_CASE(getErrorMessages); TEST_CASE(unknownMacroNoDecl); // #2638 - not variable declaration: 'AAA a[0] = 0;' // Access array and then check if the used index is within bounds TEST_CASE(arrayIndexThenCheck); TEST_CASE(bufferNotZeroTerminated); TEST_CASE(readlink); TEST_CASE(readlinkat); TEST_CASE(writeOutsideBufferSize) TEST_CASE(negativeMemoryAllocationSizeError) // #389 } 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 f()\n" "{\n" " char *str = new char[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()); } 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" " {\n" " }\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", 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()); } void array_index_7() { check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static void f(struct ABC *abc)\n" "{\n" " abc->str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_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()); // #3168 checkstd("void a(char *p) { memset(p,0,100); }\n" "void b() {\n" " char buf[10];\n" " a(buf);" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Buffer is accessed out of bounds: buf\n", errout.str()); } void array_index_11() { check("class ABC\n" "{\n" "public:\n" " ABC();\n" " char *str[10];\n" " struct ABC *next();" "};\n" "\n" "static void f()\n" "{\n" " ABC *abc1;\n" " for ( ABC *abc = abc1; abc; abc = abc->next() )\n" " {\n" " abc->str[10] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_12() { check("class Fred\n" "{\n" "private:\n" " char str[10];\n" "public:\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{\n" " str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char str[10];\n" "public:\n" " char c();\n" "};\n" "char Fred::c()\n" "{\n" " return str[10];\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_13() { check("void f()\n" "{\n" " char buf[10];\n" " for (int i = 0; i < 100; i++)\n" " {\n" " if (i < 10)\n" " int x = buf[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_14() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i+10] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); } void array_index_15() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[10+i] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); } void array_index_16() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i+1] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_17() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i*2] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 18, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int a[12];\n" " for (int i = 0; i < 12; i+=6)\n" " a[i+5] = i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[12];\n" " for (int i = 0; i < 12; i+=6)\n" " a[i+6] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[12]' accessed at index 12, which is out of bounds.\n", errout.str()); check("void f() {\n" // #4398 " int a[2];\n" " for (int i = 0; i < 4; i+=2)\n" " a[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout.str()); check("void f() {\n" // #4398 " int a[2];\n" " for (int i = 0; i < 4; i+=2)\n" " do_stuff(a+i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_18() { check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i+=1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i=4;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[6];\n" " for (int i = 0; i < 7; i++)\n" " {\n" " a[i] = i;\n" " i+=1;\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Buffer overrun\n", "", errout.str()); } void array_index_19() { // "One Past the End" is legal, as long as pointer is not dereferenced. check("void f()\n" "{\n" " char a[2];\n" " char *end = &(a[2]);\n" "}"); ASSERT_EQUALS("", errout.str()); // Getting more than one past the end is not legal check("void f()\n" "{\n" " char a[2];\n" " char *end = &(a[3]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 3, which is out of bounds.\n", errout.str()); } void array_index_20() { check("void f()\n" "{\n" " char a[8];\n" " int b[10];\n" " for ( int i = 0; i < 9; i++ )\n" " b[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_21() { check("class A {\n" " int indices[2];\n" " void foo(int indices[3]);\n" "};\n" "\n" "void A::foo(int indices[3]) {\n" " for(int j=0; j<3; ++j) {\n" " int b = indices[j];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_22() { check("int main() {\n" " size_t indices[2];\n" " int b = indices[2];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'indices[2]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_23() { check("void foo()\n" "{\n" " char c[10];\n" " c[1<<23]='a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'c[10]' accessed at index 8388608, which is out of bounds.\n", errout.str()); } void array_index_24() { // ticket #1492 and #1539 // 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() { // ticket #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:14]: (error) Array 'ptest.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: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", 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.arr[3]' accessed at index 3, which is out of bounds.\n" "[test.cpp:10]: (error) Array 'var[0].arr[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("int f( )\n" "{\n" " struct {\n" " int arr[ 3 ];\n" " } var[ 1 ];\n" " int y=1;\n" " var[ 0 ].arr[ 0 ] = 0;\n" " var[ 0 ].arr[ 1 ] = 1;\n" " var[ 0 ].arr[ 2 ] = 2;\n" " y = var[ 0 ].arr[ 2 ];\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f( ){ \n" "struct Struct{\n" " int arr[ 3 ];\n" "};\n" "int y;\n" "Struct var;\n" "var.arr[ 0 ] = 0;\n" "var.arr[ 1 ] = 1;\n" "var.arr[ 2 ] = 2;\n" "var.arr[ 3 ] = 3;\n" // <-- array access out of bounds "y=var.arr[ 3 ];\n" // <-- array access out of bounds "return y;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n" "[test.cpp:11]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("void f( ) {\n" "struct S{\n" " int var[ 3 ];\n" "} ;\n" "S var[2];\n" "var[0].var[ 0 ] = 0;\n" "var[0].var[ 1 ] = 1;\n" "var[0].var[ 2 ] = 2;\n" "var[0].var[ 4 ] = 4;\n" // <-- array access out of bounds "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'var.var[3]' accessed at index 4, which is out of bounds.\n" "[test.cpp:9]: (error) Array 'var[0].var[3]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f( ) {\n" "struct S{\n" " int var[ 3 ];\n" "} ;\n" "S var[2];\n" "var[0].var[ 0 ] = 0;\n" "var[0].var[ 1 ] = 1;\n" "var[0].var[ 2 ] = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid FPs (modified examples taken from #3838) check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab;\n" " int * p = &ab.a[10]; \n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab[1];\n" " int * p = &ab[0].a[10]; \n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab[1];\n" " int * p = &ab[10].a[0]; \n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'ab[1]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_44() { // #3979 (false positive) check("void f()\n" "{\n" " char buf[2];\n" " int i;\n" " for (i = 2; --i >= 0; )\n" " {\n" " buf[i] = 1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " double buf[2];\n" " for (int i = 2; i--; )\n" " {\n" " buf[i] = 2.;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_45() { // #4207 - handling of function with variable number of parameters / unnamed arguments // Variable number of arguments check("void f(const char *format, ...) {\n" " va_args args;\n" " va_start(args, format);\n" "}\n" "void test() {\n" " CHAR buffer[1024];\n" " f(\"%s\", buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); // Unnamed argument check("void f(char *) {\n" " dostuff();\n" "}\n" "void test() {\n" " char buffer[1024];\n" " f(buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Two statement for-loop void array_index_46() { // #4840 check("void bufferAccessOutOfBounds2() {\n" " char *buffer[]={\"a\",\"b\",\"c\"};\n" " for(int i=3; i--;) {\n" " printf(\"files(%i): %s\n\", 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_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()); } 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" "}"); 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" "}"); 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()); } void array_index_same_struct_and_var_name() { // don't throw internal error check("struct tt {\n" " char name[21];\n" "} ;\n" "void doswitch(struct tt *x)\n" "{\n" " struct tt *tt=x;\n" " tt->name;\n" "}"); ASSERT_EQUALS("", errout.str()); // detect error check("struct tt {\n" " char name[21];\n" "} ;\n" "void doswitch(struct tt *x)\n" "{\n" " struct tt *tt=x;\n" " tt->name[22] = 123;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'tt.name[21]' accessed at index 22, which is out of bounds.\n", errout.str()); } void array_index_valueflow() { check("void f(int i) {\n" " char str[3];\n" " str[i] = 0;\n" " if (i==10) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Array 'str[3]' accessed at index 10, which is out of bounds. Otherwise condition 'i==10' is redundant.\n", errout.str()); check("void f() {\n" " char str[3];\n" " str[((unsigned char)3) - 1] = 0;\n" "}", false, "test.cpp", false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #5416 FP " char *str[3];\n" " do_something(&str[0][5]);\n" "}", false, "test.cpp", false); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_1_posix_functions() { checkposix("void f(int fd)\n" "{\n" " char str[3];\n" " read(fd, str, 3);\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(int fd)\n" "{\n" " char str[3];\n" " read(fd, str, 4);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkposix("void f(int fd)\n" "{\n" " char str[3];\n" " write(fd, str, 3);\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(int fd)\n" "{\n" " char str[3];\n" " write(fd, str, 4);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkposix("void f()\n" "{\n" " long bb[2];\n" " write(stdin, bb, sizeof(bb));\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f()\n" "{\n" "char str[3];\n" "recv(s, str, 4, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkposix("void f()\n" "{\n" "char str[3];\n" "recvfrom(s, str, 4, 0, 0x0, 0x0);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkposix("void f()\n" "{\n" "char str[3];\n" "send(s, str, 4, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkposix("void f()\n" "{\n" "char str[3];\n" "sendto(s, str, 4, 0, 0x0, 0x0);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); } void buffer_overrun_1_standard_functions() { check("void f()\n" "{\n" " char str[3];\n" " strcpy(str, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkstd("void f() {\n" " char str[3];\n" " fgets(str, 3, stdin);\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f() {\n" " char str[3];\n" " fgets(str, 4, stdin);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); // fread checkstd("void f(FILE* fd) {\n" " char str[3];\n" " fread(str,1,4,fd);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkstd("void f(FILE* fd) {\n" " char str[3];\n" " fread(str,1,3,fd);\n" "}"); ASSERT_EQUALS("", errout.str()); // fwrite checkstd("void f(FILE* fd) {\n" " char str[3];\n" " fwrite(str,1,4,fd);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkstd("void f(FILE* fd) {\n" " char str[3];\n" " fwrite(str,1,3,fd);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4968 - not standard function checkstd("void f() {\n" " char str[3];\n" " foo.memset(str, 0, 100);\n" " foo::memset(str, 0, 100);\n" " std::memset(str, 0, 100);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", errout.str()); // #5257 - check strings checkstd("void f() {\n" " memcpy(temp, \"hello world\", 20);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Buffer is accessed out of bounds.\n", errout.str()); checkstd("void f() {\n" " memcpy(temp, \"abc\", 4);\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_12() { // ticket #900 check("void f() {\n" " char *a = new char(30);\n" " sprintf(a, \"%s\", \"b\");\n" " delete a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void buffer_overrun_13() { // ticket #836 checkstd("void f() {\n" " char a[10];\n" " memset(a+5, 0, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str()); checkstd("void f() {\n" " char a[10];\n" " memmove(a, a+5, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str()); } void buffer_overrun_14() { checkstd("void f(char *a) {\n" " char *b = new char[strlen(a)];\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); checkstd("void f(char *a) {\n" " char *b = new char[strlen(a) + 1];\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f(char *a) {\n" " char *b = new char[strlen(a)];\n" " a[0] = '\\0';\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " b = realloc(b, 10000);\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); checkstd("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " {\n" " strcpy(b, a);\n" " }\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str()); checkstd("void f(char *a) {\n" " char *b = malloc(strlen(a) + 1);\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f(char *a, char *c) {\n" " char *b = realloc(c, strlen(a));\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); checkstd("void f(char *a, char *c) {\n" " char *b = realloc(c, strlen(a) + 1);\n" " strcpy(b, a);\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " sprintf(b, \"%s\", a);\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void buffer_overrun_15() { // ticket #1787 check("class A : public B {\n" " char val[12];\n" " void f(int i, int ii);\n" "};\n" "void A::f(int i, int ii)\n" "{\n" " sprintf(val, \"drive_%d_partition_%d_size\", i, ii) ;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds.\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_17() { // ticket #2548 check("void f() {\n" " char t[8];\n" " sprintf(t, \"%s\", \"foo bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", 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_20() { // #2986(segmentation fault) check("x[y]\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_22() { // ticket #3124 checkstd("class A {\n" "public:\n" " char b[5][6];\n" "};\n" "int main() {\n" " A a;\n" " memset(a.b, 0, 5 * 6);\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("class A {\n" "public:\n" " char b[5][6];\n" "};\n" "int main() {\n" " A a;\n" " memset(a.b, 0, 6 * 6);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: a.b\n", errout.str()); } void buffer_overrun_23() { // ticket #3153 checkstd("void foo() {\n" " double dest = 23.0;\n" " char* const source = (char*) malloc(sizeof(dest));\n" " memcpy(&dest, source + sizeof(double), sizeof(dest));\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); checkstd("void foo() {\n" " double dest = 23.0;\n" " char* const source = (char*) malloc(2 * sizeof(dest));\n" " memcpy(&dest, source + sizeof(double), sizeof(dest));\n" "}"); ASSERT_EQUALS("", 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()); } 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()); } 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 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 result does not point into or just past the end of the array.\n", 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 result does not point into or just past the end of the buffer.\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 sprintf1() { check("void f()\n" "{\n" " char str[3];\n" " sprintf(str, \"%s\", \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char * c = new char[10];\n" " sprintf(c, \"%s\", \"/usr/LongLongLongLongUserName/bin/LongLongApplicationName\");\n" " delete [] c;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void sprintf2() { check("int getnumber();\n" "void f()\n" "{\n" " char str[5];\n" " sprintf(str, \"%d: %s\", getnumber(), \"abcde\");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void sprintf3() { check("void f()\n" "{\n" " char str[3];\n" " sprintf(str, \"test\");\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char str[5];\n" " sprintf(str, \"test%s\", \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf4() { // ticket #690 check("void f()\n" "{\n" " char a[3];\n" " sprintf(a, \"%02ld\", 99);\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf5() { // ticket #729 check("void f(int condition)\n" "{\n" " char buf[3];\n" " sprintf(buf, \"%s\", condition ? \"11\" : \"22\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf6() { check("void f(int condition)\n" "{\n" " char buf[3];\n" " sprintf(buf, \"%s\", condition ? \"11\" : \"222\");\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); } void sprintf7() { check("struct Foo { char a[1]; };\n" "void f()\n" "{\n" " struct Foo x;\n" " sprintf(x.a, \"aa\");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (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" "{\n" " struct Foo *x = malloc(sizeof(Foo));\n" " sprintf(x.a, \"aa\");\n" " free(x);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct Foo { char a[1]; };\n" "void f()\n" "{\n" " struct Foo *x = malloc(sizeof(Foo) + 10);\n" " sprintf(x.a, \"aa\");\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf8() { check("struct Foo { char a[3]; };\n" "void f()\n" "{\n" " struct Foo x;\n" " sprintf(x.a, \"aa\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf9() { check("void f()\n" "{\n" " gchar str[3];\n" " sprintf(str, \"1\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf10() { check("void f()\n" "{\n" " TString str = \"\";\n" " sprintf(str, \"1\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf1() { check("void f()\n" "{\n" " char str[5];\n" " snprintf(str, 10, \"%s\", \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) snprintf size is out of bounds: Supplied size 10 is larger than actual size 5.\n", errout.str()); } void snprintf2() { check("void f()\n" "{\n" " char str[5];\n" " snprintf(str, 5, \"%s\", \"abc\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf4() { check("void f(int x)\n" "{\n" " char str[5];\n" " snprintf(str, 8 - x, \"abcdefghijkl\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf5() { check("struct Foo { char a[1]; };\n" "void f()\n" "{\n" " struct Foo x;\n" " snprintf(x.a, 2, \"aa\");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) snprintf size is out of bounds: Supplied size 2 is larger than actual size 1.\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" "{\n" " struct Foo *x = malloc(sizeof(Foo));\n" " snprintf(x.a, 2, \"aa\");\n" " free(x);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct Foo { char a[1]; };\n" "void f()\n" "{\n" " struct Foo *x = malloc(sizeof(Foo) + 10);\n" " snprintf(x.a, 2, \"aa\");\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf6() { check("struct Foo { char a[3]; };\n" "void f()\n" "{\n" " struct Foo x;\n" " snprintf(x.a, 2, \"aa\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf7() { check("void x() {\n" " sal_Char pString[1024];\n" " snprintf(pString, 1024, \"ab\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void strncat1() { checkstd("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() { checkstd("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() { checkstd("struct Foo { char a[4]; };\n" "void f(char *a) {\n" " struct Foo x;\n" " strncat(x.a, a, 5);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: x.a\n", errout.str()); } void strncat4() { checkstd("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() { checkstd("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() { checkstd("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() { checkstd("void f() {\n" " INT str[10];\n" " strcat(str, \"aa\");\n" "}"); ASSERT_EQUALS("", errout.str()); } // memchr/memset/memcpy/etc void memfunc1() { checkstd("struct S {\n" " char a[5];\n" "};\n" "void f() {\n" " S s;\n" " memset(s.a, 0, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: s.a\n", errout.str()); checkstd("void f() {\n" " char str[5];\n" " memset(str, 0, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); checkstd("void f() {\n" " char a[5], b[50];\n" " memcpy(a, b, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str()); checkstd("void f() {\n" " char a[5], b[50];\n" " memmove(a, b, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str()); // Ticket #909 checkstd("void f() {\n" " char * pch;\n" " char str[] = \"Example string\";\n" " pch = memchr (str, 'p', 16);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str()); } // ticket #2121 - buffer access out of bounds when using uint32_t void memfunc2() { checkstd("void f() {\n" " unknown_type_t buf[4];\n" " memset(buf, 0, 100);\n" "}"); ASSERT_EQUALS("", errout.str()); } // ticket #1659 - overflowing variable when using memcpy void memfunc3() { checkstd("void f() { \n" " char str1[]=\"Sample string\";\n" " char str2;\n" " memcpy (&str2,str1,13);\n" // <-- strlen(str1)+1 = 13 "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str1\n","", errout.str()); checkstd("void f() {\n" " char a[10];\n" " char str1[] = \"abcdef\";\n" " memset(a, 0, 11);\n" // <-- strlen(str1) + 5 = 11 "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n", errout.str()); checkstd("void f() { \n" "char str1[]=\"Sample string\";\n" "char str2;\n" "memcpy (&str2,str1,15);\n" // <-- strlen(str1) + 1 = 15 "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str1\n", errout.str()); checkstd("void f() { \n" " char str[5];\n" " memcpy (str, \"\\0\\0\\0\\0\\0\", 5);\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() { checkstd("void foo()\n" "{\n" " char str[10];\n" " if (str[0])\n" " {\n" " char str[50];\n" " memset(str,0,50);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid3() { // #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 alloc1() { check("void foo()\n" "{\n" " char *s; s = new char[10];\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // ticket #1670 - false negative when using return check("char f()\n" "{\n" " char *s; s = new int[10];\n" " return s[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("struct Fred { char c[10]; };\n" "char f()\n" "{\n" " Fred *f; f = new Fred;\n" " return f->c[10];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'f.c[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void foo()\n" "{\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" "{\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:7]: (error) Array 'buf[9]' accessed at index 9, 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" "}", false, "test.cpp", false); 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" "}", false, "test.cpp", false); ASSERT_EQUALS("", errout.str()); } // data is allocated with malloc void alloc2() { 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" "}", false, "test.cpp", false); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'tab4[20]' accessed at index 20, 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" "}", false, "test.cpp", false); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } // statically allocated buffer void alloc3() { 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 alloc4() { 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 malloc_memset() { checkstd("void f() {\n" " char *p = malloc(10);\n" " memset(p,0,100);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void memset1() { checkstd("void foo()\n" "{\n" " char s[10];\n" " memset(s, 5, '*');\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) The size argument is given as a char constant.\n", errout.str()); checkstd("void foo()\n" "{\n" " int* x[5];\n" " memset(x, 0, sizeof(x));\n" "}"); ASSERT_EQUALS("", errout.str()); } void memset2() { check("class X {\n" " char* array[2];\n" " X();\n" "};\n" "X::X() {\n" " memset(array, 0, sizeof(array));\n" "}",false,"test.cpp",false); ASSERT_EQUALS("", errout.str()); } void counter_test() 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); strTok.str("\"12345\""); std::list stringAsParameter; stringAsParameter.push_back(&strTok); 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 strncpy1() { checkstd("void f() {\n" " char c[7];\n" " strncpy(c, \"hello\", 7);\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f() {\n" " char c[6];\n" " strncpy(c,\"hello\",6);\n" "}"); ASSERT_EQUALS("", errout.str()); checkstd("void f() {\n" " char c[5];\n" " strncpy(c,\"hello\",6);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); checkstd("void f() {\n" " char c[6];\n" " strncpy(c,\"hello!\",7);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); checkstd("struct AB { char a[10]; };\n" "void foo(AB *ab) {\n" " strncpy(x, ab->a, 100);\n" "}"); ASSERT_EQUALS("", 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()); // 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("struct C {} {} x"); } void crash3() { check("void a(char *p) {\n" " f( { if(finally_arg); } );\n" "}\n" "\n" "void b() {\n" " char arr[64];\n" " a(arr);\n" "}"); } void crash4() { check("struct b { unknown v[0]; };\n" "void d() { struct b *f; f = malloc(108); }"); } void crash5() { check("static f() { int i; int source[1] = { 1 }; for (i = 0; i < 4; i++) (u, if (y u.x e)) }", true, "test.cpp", false); // Garbage code } void garbage1() { // Ticket #5203 check("int f ( int* r ) { { int s[2] ; f ( s ) ; if ( ) } }"); } void epcheck(const char code[], const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); Settings settings; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); tokenizer.simplifyTokenList2(); // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun(&tokenizer, &settings, this); checkBufferOverrun.bufferOverrun(); } void executionPaths1() { epcheck("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()); epcheck("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() { epcheck("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() { epcheck("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 executionPaths4() { // Ticket #2386 - Segmentation fault upon strange syntax ASSERT_THROW(epcheck("void f() {\n" " switch ( x ) {\n" " case struct Tree : break;\n" " }\n" "}"), InternalError); } void executionPaths5() { // No false positive epcheck("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" "}"; epcheck(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[])\n" "{\n" " char prog[10];\n" " sprintf(prog, \"%s\", 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 **argv, char **envp)\n" "{\n" " char prog[10];\n" " sprintf(prog, \"%s\", 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" " sprintf(prog, \"%s\", *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" " sprintf(prog, \"%p\", 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()); } 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 unknownMacroNoDecl() { check("void f() {\n" " int a[10];\n" " AAA a[0] = 0;\n" // <- not a valid array declaration " a[1] = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } 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()); // this one doesn't work for now, hopefully in the future check("void f(const int a[], unsigned i) {\n" " if(a[i] < func(i) && i <= 42) {\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", "", 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 readlink() { check("void f() {\n" " char buf[255];\n" " ssize_t len = readlink(path, buf, 254);\n" " printf(\"%s\n\", buf);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' is not null-terminated after the call to readlink().\n", "", errout.str()); // C only: Primitive pointer simplification check("void f() {\n" " char buf[255];\n" " ssize_t len = readlink(path, &buf[0], 254);\n" " printf(\"%s\n\", buf);\n" "}\n", true, "test.c"); TODO_ASSERT_EQUALS("[test.c:3]: (warning, inconclusive) The buffer 'buf' is not null-terminated after the call to readlink().\n", "", errout.str()); check("void f() {\n" " char buf[255];\n" " ssize_t len = readlink(path, buf, 254);\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char buf[10];\n" " ssize_t len = readlink(path, buf, 255);\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) readlink() buf size is out of bounds: Supplied size 255 is larger than actual size 10.\n", errout.str()); check("void f() {\n" " char buf[255];\n" " ssize_t len = readlink(path, buf, 255);\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) readlink() might return the full size of 'buf'. Lower the supplied size by one.\n", errout.str()); check("void f() {\n" " char buf[255];\n" " ssize_t len = readlink(path, buf, 254);\n" " if (len == -1) {\n" " return;\n" " }\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char buf[255] = {0};\n" " readlink(path, buf, 254);\n" // <- doesn't write whole buf " puts(buf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void readlinkat() { check("void f() {\n" " char buf[255];\n" " ssize_t len = readlinkat(42, path, buf, 254);\n" " printf(\"%s\n\", buf);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' is not null-terminated after the call to readlinkat().\n", "", errout.str()); check("void f() {\n" " char buf[255];\n" " ssize_t len = readlinkat(42, path, buf, 254);\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char buf[10];\n" " ssize_t len = readlinkat(42, path, buf, 255);\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) readlinkat() buf size is out of bounds: Supplied size 255 is larger than actual size 10.\n", errout.str()); check("void f() {\n" " char buf[255];\n" " ssize_t len = readlinkat(42, path, buf, 255);\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) readlinkat() might return the full size of 'buf'. Lower the supplied size by one.\n", errout.str()); check("void f() {\n" " char buf[255];\n" " ssize_t len = readlinkat(42, path, buf, 254);\n" " if (len == -1) {\n" " return;\n" " }\n" " buf[len] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void writeOutsideBufferSize() { check("void f(void){\n" "write(1, \"Dump string \\n\", 100);\n" "}"); // ^ number of bytes too big ASSERT_EQUALS("[test.cpp:2]: (error) Writing 86 bytes outside buffer size.\n", errout.str()); check("void f(void){\n" "write(1, \"Dump string \\n\", 10);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4706 avoid crashing when a struct member is used as first argument check("static struct {\n" " int i[2];\n" "} p;\n" "void foo()\n" "{\n" " write(p.i[1], \"\", 1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static struct {\n" " int i[2];\n" "} p;\n" "void foo()\n" "{\n" " write(p.i[1], \"\", 2);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Writing 1 bytes outside buffer size.\n", errout.str()); // #4969 check("void foo()\n" "{\n" " write(1, \"\\0\", 1);\n" "}"); ASSERT_EQUALS("", errout.str()); // that is documented to be ok check("void foo()\n" "{\n" " write(1, 0, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // ... that is not ok check("void foo()\n" "{\n" " write(1, 0, 1);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Writing 1 bytes outside buffer size.\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()); } }; REGISTER_TEST(TestBufferOverrun) cppcheck-1.66/test/testcharvar.cpp000066400000000000000000000153451236713773000172600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkother.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestCharVar : public TestFixture { public: TestCharVar() : TestFixture("TestCharVar") { } private: void run() { TEST_CASE(array_index_1); TEST_CASE(array_index_2); TEST_CASE(array_index_3); TEST_CASE(bitop1); TEST_CASE(bitop2); TEST_CASE(bitop3); TEST_CASE(bitop4); // (long)&c TEST_CASE(return1); TEST_CASE(assignChar); TEST_CASE(and03); TEST_CASE(pointer); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); // 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]: (warning) Signed 'char' type used as array index.\n", 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(char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Signed 'char' type used as array index.\n", 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 array_index_3() { // only write error message when array is more than // 0x80 elements in size. Otherwise the full valid // range is accessible with a char. check("char buf[0x81];\n" "void bar(char c) {\n" " buf[c] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Signed 'char' type used as array index.\n", errout.str()); check("char buf[0x80];\n" "void bar(char c) {\n" " buf[c] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar(char c) {\n" " buf[c] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void bitop1() { check("void foo()\n" "{\n" " int result = 0;\n" " char ch;\n" " result = a | ch;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); } void bitop2() { check("void foo()\n" "{\n" " char ch;\n" " func(&ch);\n" "}"); ASSERT_EQUALS("", errout.str()); } void bitop3() { check("void f(int& i, char& c) {\n" " i &= c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); } void bitop4() { check("long f(char c) {\n" " long a;\n" " a = (long)&c;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void return1() { check("void foo()\n" "{\n" " char c;\n" " return &c;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignChar() { check("void foo()\n" "{\n" " char c;\n" " c = c & 0x123;\n" "}"); ASSERT_EQUALS("", errout.str()); } void and03() { check("void foo()\n" "{\n" " char c;\n" " int i = c & 0x03;\n" "}"); ASSERT_EQUALS("", errout.str()); } void pointer() { // ticket #2866 check("void f(char *p) {\n" " int ret = 0;\n" " ret |= *p;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); // fixed code check("void f(char *p) {\n" " int ret = 0;\n" " ret |= (unsigned char)*p;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3872 - false positive check("int f(int *p) {\n" " int ret = a();\n" " ret |= *p;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3878 - false positive check("int f(unsigned char *p) {\n" " int ret = a();\n" " ret |= *p;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestCharVar) cppcheck-1.66/test/testclass.cpp000066400000000000000000007315431236713773000167440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkclass.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestClass : public TestFixture { public: TestClass() : TestFixture("TestClass") { } private: void run() { 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(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(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); // #5901 - std::uint8_t 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(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(initializerListOrder); TEST_CASE(initializerListUsage); TEST_CASE(forwardDeclaration); // ticket #4290/#3190 TEST_CASE(pureVirtualFunctionCall); TEST_CASE(pureVirtualFunctionCallOtherClass); TEST_CASE(pureVirtualFunctionCallWithBody); TEST_CASE(pureVirtualFunctionCallPrevented); TEST_CASE(duplInheritedMembers); TEST_CASE(invalidInitializerList); } void checkDuplInheritedMembers(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.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()); } void checkCopyConstructor(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"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings, 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()); } 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(""); Settings settings; settings.addEnabled("style"); settings.inconclusive = true; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings, 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" "private:\n" " void operator=(const A&);\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()); } 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(""); Settings settings; settings.addEnabled("style"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings, 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]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "szp &szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:5]: (style) 'operator=' should return reference to 'this' instance.\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]: (style) 'operator=' should return reference to 'this' instance.\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]: (style) 'operator=' should return reference to 'this' instance.\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(""); 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.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(""); Settings settings; settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings, 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' 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 checkNoConstructor(const char code[], const char* level="style") { // Clear the error log errout.str(""); Settings settings; settings.addEnabled(level); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.constructors(); } void noConstructor1() { // There are nonstatic member variables - constructor is needed checkNoConstructor("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() { checkNoConstructor("class Fred\n" "{\n" "public:\n" " static void foobar();\n" "};\n" "\n" "void Fred::foobar()\n" "{ }"); ASSERT_EQUALS("", errout.str()); } void noConstructor3() { checkNoConstructor("class Fred\n" "{\n" "private:\n" " static int foobar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor4() { checkNoConstructor("class Fred\n" "{\n" "public:\n" " int foobar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor5() { checkNoConstructor("namespace Foo\n" "{\n" " int i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void noConstructor6() { // ticket #4386 checkNoConstructor("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 checkNoConstructor("short bar;\n" "class foo;\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor8() { // ticket #4404 checkNoConstructor("class LineSegment;\n" "class PointArray { };\n" "void* tech_ = NULL;\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor9() { // ticket #4419 checkNoConstructor("class CGreeting : public CGreetingBase {\n" "public:\n" " CGreeting() : CGreetingBase(), MessageSet(false) {}\n" "private:\n" " bool MessageSet;\n" "};"); ASSERT_EQUALS("", errout.str()); } void checkNoMemset(const char code[], bool load_std_cfg = false) { // Clear the error log errout.str(""); Settings settings; settings.addEnabled("warning"); if (load_std_cfg) { LOAD_LIB_2(settings.library, "std.cfg"); } // 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" "}"); TODO_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()); } 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()); checkNoMemset("class A {\n" " std::array ints;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}", true); ASSERT_EQUALS("", errout.str()); // std::array is POD (#5481) } void memsetOnStdPodType() { // Ticket #5901 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" "}", true); 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(""); 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.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[], const Settings *s = 0, bool inconclusive = true, bool verify = true) { // Clear the error log errout.str(""); // Check.. Settings settings; if (s) settings = *s; else settings.addEnabled("style"); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); if (verify && str1 != str2) warn(("Unsimplified code in test case\nstr1="+str1+"\nstr2="+str2).c_str()); CheckClass checkClass(&tokenizer, &settings, 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() { checkConst("struct Fred {\n" " int array[10];\n" " typedef int* (Fred::*UnspecifiedBoolType);\n" " operator UnspecifiedBoolType() { };\n" "};"); 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" "};\n" ); 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 bool(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" "};"); 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", 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("[test.cpp:4] -> [test.cpp:2]: (performance, inconclusive) Technically the member function 'MixerParticipant::GetAudioFrame' can be static.\n", errout.str()); checkConst("class MixerParticipant : public MixerParticipant {\n" " bool InitializeFileReader() {\n" " printf(\"music\");\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MixerParticipant::InitializeFileReader' can be static.\n", 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"); 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 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()); } 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" "};"; Settings settings; settings.addEnabled("style"); checkConst(code, &settings, true); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'foo::f' can be static.\n", errout.str()); checkConst(code, &settings, 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 checkInitializerListOrder(const char code[]) { // Clear the error log errout.str(""); // Check.. Settings settings; settings.addEnabled("style"); settings.inconclusive = true; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, &settings, 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()); } 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("class Fred {\n" " int a;\n" // No message for builtin types: No performance gain " int* b;\n" // No message for pointers: No performance gain " Fred() { a = 0; b = 0; }\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()); } // 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() { checkConst("class foo;\n" "int bar;\n"); ASSERT_EQUALS("", errout.str()); checkConst("class foo;\n" "class foo;\n"); ASSERT_EQUALS("", errout.str()); checkConst("class foo{};\n" "class foo;\n"); ASSERT_EQUALS("", errout.str()); } void checkPureVirtualFunctionCall(const char code[], const Settings *s = 0, bool inconclusive = true) { // Clear the error log errout.str(""); // Check.. Settings settings; if (s) settings = *s; else settings.addEnabled("style"); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, &settings, 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();}\n"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); // ticket # 5831 checkPureVirtualFunctionCall("class abc {\n" "public:\n" " virtual ~abc() throw() {}\n" " virtual void def(void* g) throw () = 0;\n" "};\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 invalidInitializerList() { ASSERT_THROW(checkNoConstructor("struct R1 {\n" " int a;\n" " R1 () : a { }\n" "};\n", "warning"), InternalError); } }; REGISTER_TEST(TestClass) cppcheck-1.66/test/testcmdlineparser.cpp000066400000000000000000001073751236713773000204670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "cmdlineparser.h" #include "settings.h" #include "redirect.h" #include "timer.h" 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(xmlver1); 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._errorsOnly = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings._errorsOnly); } void quietlong() { REDIRECT; const char *argv[] = {"cppcheck", "--quiet", "file.cpp"}; settings._errorsOnly = false; ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT_EQUALS(true, settings._errorsOnly); } 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("style")); ASSERT(settings.isEnabled("warning")); ASSERT(settings.isEnabled("unusedFunction")); ASSERT(settings.isEnabled("missingInclude")); ASSERT(!settings.isEnabled("internal")); } void enabledStyle() { REDIRECT; const char *argv[] = {"cppcheck", "--enable=style", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.isEnabled("style")); ASSERT(settings.isEnabled("warning")); ASSERT(settings.isEnabled("performance")); ASSERT(settings.isEnabled("portability")); ASSERT(!settings.isEnabled("unusedFunction")); ASSERT(!settings.isEnabled("missingInclude")); } void enabledPerformance() { REDIRECT; const char *argv[] = {"cppcheck", "--enable=performance", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(!settings.isEnabled("style")); ASSERT(!settings.isEnabled("warning")); ASSERT(settings.isEnabled("performance")); ASSERT(!settings.isEnabled("portability")); ASSERT(!settings.isEnabled("unusedFunction")); ASSERT(!settings.isEnabled("missingInclude")); } void enabledPortability() { REDIRECT; const char *argv[] = {"cppcheck", "--enable=portability", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(!settings.isEnabled("style")); ASSERT(!settings.isEnabled("warning")); ASSERT(!settings.isEnabled("performance")); ASSERT(settings.isEnabled("portability")); ASSERT(!settings.isEnabled("unusedFunction")); ASSERT(!settings.isEnabled("missingInclude")); } void enabledUnusedFunction() { REDIRECT; const char *argv[] = {"cppcheck", "--enable=unusedFunction", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.isEnabled("unusedFunction")); } void enabledMissingInclude() { REDIRECT; const char *argv[] = {"cppcheck", "--enable=missingInclude", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.isEnabled("missingInclude")); } #ifdef CHECK_INTERNAL void enabledInternal() { REDIRECT; const char *argv[] = {"cppcheck", "--enable=internal", "file.cpp"}; settings = Settings(); ASSERT(defParser.ParseFromArgs(3, argv)); ASSERT(settings.isEnabled("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("style")); ASSERT(settings.isEnabled("warning")); ASSERT(!settings.isEnabled("performance")); ASSERT(settings.isEnabled("portability")); ASSERT(!settings.isEnabled("unusedFunction")); ASSERT(settings.isEnabled("missingInclude")); } 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,unnecessaryQualification", "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("unnecessaryQualification", "file.cpp", 1U)); } void suppressionTwoSeparate() { REDIRECT; const char *argv[] = {"cppcheck", "--suppress=uninitvar", "--suppress=unnecessaryQualification", "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("unnecessaryQualification", "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 xmlver1() { REDIRECT; const char *argv[] = {"cppcheck", "--xml-version=1", "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.66/test/testconstructors.cpp000066400000000000000000002741561236713773000204110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkclass.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestConstructors : public TestFixture { public: TestConstructors() : TestFixture("TestConstructors") { } private: void check(const char code[], bool showAll = false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.inconclusive = showAll; settings.addEnabled("style"); settings.addEnabled("warning"); // 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() { 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 TEST_CASE(simple12); // ticket #4620 TEST_CASE(simple13); // #5498 - no constructor, c++11 assignments 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(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(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(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 } 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 check("class Fred {\n" "public:\n" " Fred() {}\n" "private:\n" " int x = 0;\n" "};"); ASSERT_EQUALS("", 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 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()); } 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 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()); } 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 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()); } 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 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()); } 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()); } 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("#file \"fred.h\"\n" "class Fred\n" "{\n" "private:\n" " unsigned int i;\n" "public:\n" " Fred();\n" "};\n" "#endfile"); ASSERT_EQUALS("", errout.str()); } void uninitVarHeader2() { check("#file \"fred.h\"\n" "class Fred\n" "{\n" "private:\n" " unsigned int i;\n" "public:\n" " Fred() { }\n" "};\n" "#endfile"); ASSERT_EQUALS("[fred.h:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarHeader3() { check("#file \"fred.h\"\n" "class Fred\n" "{\n" "private:\n" " mutable int i;\n" "public:\n" " Fred() { }\n" "};\n" "#endfile"); 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()); } }; REGISTER_TEST(TestConstructors) cppcheck-1.66/test/testcppcheck.cpp000066400000000000000000000061031236713773000174020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "cppcheckexecutor.h" #include "testsuite.h" #include "path.h" #include "check.h" #include #include #include extern std::ostringstream errout; extern std::ostringstream output; 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[info.length()-1]); // \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()); // TODO: 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.66/test/testdivision.cpp000066400000000000000000000146701236713773000174560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 dangerous division.. // such as "svar / uvar". Treating "svar" as unsigned data is not good #include "tokenize.h" #include "checkother.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestDivision : public TestFixture { public: TestDivision() : TestFixture("TestDivision") { } private: void check(const char code[], bool inconclusive = false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for unsigned divisions.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkUnsignedDivision(); } void run() { TEST_CASE(division1); TEST_CASE(division2); TEST_CASE(division4); TEST_CASE(division5); TEST_CASE(division6); TEST_CASE(division7); TEST_CASE(division8); TEST_CASE(division9); TEST_CASE(division10); TEST_CASE(division11); // no error when using "unsigned char" (it is promoted) } void division1() { check("void f() {\n" " int ivar = -2;\n" " unsigned int uvar = 2;\n" " return ivar / uvar;\n" "}", false); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int ivar = -2;\n" " unsigned int uvar = 2;\n" " return ivar / uvar;\n" "}", true); ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Division with signed and unsigned operators. The result might be wrong.\n", errout.str()); } void division2() { check("void f()\n" "{\n" " int ivar = -2;\n" " unsigned int uvar = 2;\n" " return uvar / ivar;\n" "}", true); ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Division with signed and unsigned operators. The result might be wrong.\n", errout.str()); } void division4() { check("void f1()\n" "{\n" " int i1;\n" "}\n" "\n" "void f2(unsigned int i1)\n" "{\n" " unsigned int i2;\n" " result = i2 / i1;" "}", true); ASSERT_EQUALS("", errout.str()); check("void f1()\n" "{\n" " unsigned int num = 0;\n" "}\n" "\n" "void f2(int X)\n" "{\n" " X = X / z;" "}", true); ASSERT_EQUALS("", errout.str()); } void division5() { check("void foo()\n" "{\n" " unsigned int val = 32;\n" " val = val / (16);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void division6() { check("void foo()\n" "{\n" " unsigned int val = 32;\n" " int i = val / -2; }"); ASSERT_EQUALS("[test.cpp:4]: (error) Unsigned division. The result will be wrong.\n", errout.str()); } void division7() { check("void foo()\n" "{\n" " unsigned int val = 32;\n" " int i = -96 / val; }"); ASSERT_EQUALS("[test.cpp:4]: (error) Unsigned division. The result will be wrong.\n", errout.str()); } void division8() { check("void foo(int b)\n" "{\n" " if (b > 0)\n" " {\n" " unsigned int a;\n" " unsigned int c = a / b;\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(int b)\n" "{\n" " if (b < 0)\n" " {\n" " unsigned int a;\n" " unsigned int c = a / b;\n" " }\n" "}", true); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Division with signed and unsigned operators. The result might be wrong.\n", "", errout.str()); check("void a(int i) { }\n" "int foo( unsigned int sz )\n" "{\n" " register unsigned int i=1;\n" " return i/sz;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void division9() { check("void f()\n" "{\n" " int ivar = -2;\n" " unsigned long uvar = 2;\n" " return ivar / uvar;\n" "}", true); ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Division with signed and unsigned operators. The result might be wrong.\n", errout.str()); check("void f()\n" "{\n" " int ivar = -2;\n" " unsigned long long uvar = 2;\n" " return ivar / uvar;\n" "}", true); ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Division with signed and unsigned operators. The result might be wrong.\n", errout.str()); } void division10() { // Ticket: #2932 - don't segfault check("i / i", true); ASSERT_EQUALS("", errout.str()); } void division11() { check("void f(int x, unsigned char y) {\n" " int z = x / y;\n" // no error, y is promoted "}", true); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestDivision) cppcheck-1.66/test/testerrorlogger.cpp000066400000000000000000000367561236713773000201740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "cppcheck.h" #include "testsuite.h" #include "errorlogger.h" 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(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(ToXml); TEST_CASE(ToXmlLocations); TEST_CASE(ToVerboseXml); TEST_CASE(ToVerboseXmlLocations); 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(suppressUnmatchedSuppressions); } 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, 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, 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, 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, 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, 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, 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, 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 ToXml() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS("\n", ErrorLogger::ErrorMessage::getXMLHeader(1)); ASSERT_EQUALS("", ErrorLogger::ErrorMessage::getXMLFooter(1)); ASSERT_EQUALS(" ", msg.toXML(false,1)); } void ToXmlLocations() const { std::list locs; locs.push_back(fooCpp5); locs.push_back(barCpp8); ErrorMessage msg(locs, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS("\n", ErrorLogger::ErrorMessage::getXMLHeader(1)); ASSERT_EQUALS("", ErrorLogger::ErrorMessage::getXMLFooter(1)); ASSERT_EQUALS(" ", msg.toXML(false,1)); } void ToVerboseXml() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS("\n", ErrorLogger::ErrorMessage::getXMLHeader(1)); ASSERT_EQUALS("", ErrorLogger::ErrorMessage::getXMLFooter(1)); ASSERT_EQUALS(" ", msg.toXML(true,1)); } void ToVerboseXmlLocations() const { std::list locs; locs.push_back(fooCpp5); locs.push_back(barCpp8); ErrorMessage msg(locs, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS("\n", ErrorLogger::ErrorMessage::getXMLHeader(1)); ASSERT_EQUALS("", ErrorLogger::ErrorMessage::getXMLFooter(1)); ASSERT_EQUALS(" ", msg.toXML(true,1)); } void ToXmlV2() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, Severity::error, "Programming error.\nVerbose error", "errorId", false); std::string header("\n\n"); header += " \n "; ASSERT_EQUALS(header, ErrorLogger::ErrorMessage::getXMLHeader(2)); ASSERT_EQUALS(" \n", ErrorLogger::ErrorMessage::getXMLFooter(2)); std::string message(" \n"; message += " \n "; ASSERT_EQUALS(message, msg.toXML(false, 2)); } void ToXmlV2Locations() const { std::list locs; locs.push_back(fooCpp5); locs.push_back(barCpp8); ErrorMessage msg(locs, Severity::error, "Programming error.\nVerbose error", "errorId", false); std::string header("\n\n"); header += " \n "; ASSERT_EQUALS(header, ErrorLogger::ErrorMessage::getXMLHeader(2)); ASSERT_EQUALS(" \n", ErrorLogger::ErrorMessage::getXMLFooter(2)); std::string message(" \n"; message += " \n"; message += " \n "; ASSERT_EQUALS(message, msg.toXML(false, 2)); } void ToXmlV2Encoding() const { std::list locs; ErrorMessage msg(locs, Severity::error, "Programming error.\nComparing \"\203\" with \"\003\"", "errorId", false); const std::string message(" "); ASSERT_EQUALS(message, msg.toXML(false, 2)); } void InconclusiveXml() const { // Location std::list locs(1, fooCpp5); // Inconclusive error message ErrorMessage msg(locs, Severity::error, "Programming error", "errorId", true); // Don't save inconclusive messages if the xml version is 1 ASSERT_EQUALS("", msg.toXML(false, 1)); // xml version 2 error message ASSERT_EQUALS(" \n" " \n" " ", msg.toXML(false, 2)); } void SerializeInconclusiveMessage() const { // Inconclusive error message std::list locs; ErrorMessage msg(locs, Severity::error, "Programming error", "errorId", true); ASSERT_EQUALS("7 errorId" "5 error" "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 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.66/test/testexceptionsafety.cpp000066400000000000000000000341101236713773000210330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkexceptionsafety.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestExceptionSafety : public TestFixture { public: TestExceptionSafety() : TestFixture("TestExceptionSafety") { } private: void run() { 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 settings; settings.addEnabled("all"); 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()); check("x::~x() {\n" " throw e;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (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 noexcept function.\n" "[test.cpp:3]: (error) Exception thrown in noexcept function.\n" "[test.cpp:5]: (error) Exception thrown in noexcept function.\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 throw() function.\n" "[test.cpp:4]: (error) Exception thrown in throw() function.\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 __attribute__((nothrow)) function.\n" "[test.cpp:3]: (error) Exception thrown in __attribute__((nothrow)) function.\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 __declspec(nothrow) function.\n" "[test.cpp:3]: (error) Exception thrown in __declspec(nothrow) function.\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.66/test/testfilelister.cpp000066400000000000000000000060621236713773000177700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "filelister.h" #include "settings.h" #include #ifndef _WIN32 #include #include #include #include #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); #ifndef _WIN32 TEST_CASE(absolutePath); #endif TEST_CASE(recursiveAddFiles); } void isDirectory() const { ASSERT_EQUALS(false, FileLister::isDirectory("readme.txt")); ASSERT_EQUALS(true, FileLister::isDirectory("lib")); } #ifndef _WIN32 void absolutePath() const { std::vector current_dir; #ifdef PATH_MAX current_dir.resize(PATH_MAX); #else current_dir.resize(1024); #endif while (getcwd(¤t_dir[0], current_dir.size()) == nullptr && errno == ERANGE) { current_dir.resize(current_dir.size() + 1024); } std::string absolute_path = FileLister::getAbsolutePath("."); ASSERT_EQUALS(¤t_dir[0], absolute_path); } #endif void recursiveAddFiles() const { // Recursively add add files.. std::map files; std::set extra; FileLister::recursiveAddFiles(files, ".", extra); // 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.66/test/testfiles.pri000066400000000000000000000043341236713773000167400ustar00rootroot00000000000000# no manual edits - this file is autogenerated by dmake INCLUDEPATH += ../externals/tinyxml SOURCES += $${BASEPATH}/test64bit.cpp \ $${BASEPATH}/testassert.cpp \ $${BASEPATH}/testassignif.cpp \ $${BASEPATH}/testautovariables.cpp \ $${BASEPATH}/testbool.cpp \ $${BASEPATH}/testboost.cpp \ $${BASEPATH}/testbufferoverrun.cpp \ $${BASEPATH}/testcharvar.cpp \ $${BASEPATH}/testclass.cpp \ $${BASEPATH}/testcmdlineparser.cpp \ $${BASEPATH}/testconstructors.cpp \ $${BASEPATH}/testcppcheck.cpp \ $${BASEPATH}/testdivision.cpp \ $${BASEPATH}/testerrorlogger.cpp \ $${BASEPATH}/testexceptionsafety.cpp \ $${BASEPATH}/testfilelister.cpp \ $${BASEPATH}/testincompletestatement.cpp \ $${BASEPATH}/testinternal.cpp \ $${BASEPATH}/testio.cpp \ $${BASEPATH}/testleakautovar.cpp \ $${BASEPATH}/testlibrary.cpp \ $${BASEPATH}/testmathlib.cpp \ $${BASEPATH}/testmemleak.cpp \ $${BASEPATH}/testnonreentrantfunctions.cpp \ $${BASEPATH}/testnullpointer.cpp \ $${BASEPATH}/testobsoletefunctions.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}/testsimplifytokens.cpp \ $${BASEPATH}/testsizeof.cpp \ $${BASEPATH}/teststl.cpp \ $${BASEPATH}/testsuite.cpp \ $${BASEPATH}/testsuppressions.cpp \ $${BASEPATH}/testsymboldatabase.cpp \ $${BASEPATH}/testthreadexecutor.cpp \ $${BASEPATH}/testtimer.cpp \ $${BASEPATH}/testtoken.cpp \ $${BASEPATH}/testtokenize.cpp \ $${BASEPATH}/testuninitvar.cpp \ $${BASEPATH}/testunusedfunctions.cpp \ $${BASEPATH}/testunusedprivfunc.cpp \ $${BASEPATH}/testunusedvar.cpp \ $${BASEPATH}/testvalueflow.cpp cppcheck-1.66/test/testincompletestatement.cpp000066400000000000000000000154131236713773000217120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 unused variables.. #include "testsuite.h" #include "tokenize.h" #include "checkother.h" #include extern std::ostringstream errout; class TestIncompleteStatement : public TestFixture { public: TestIncompleteStatement() : TestFixture("TestIncompleteStatement") { } private: void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check for incomplete statements.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkIncompleteStatement(); } void run() { TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); TEST_CASE(test4); TEST_CASE(test5); TEST_CASE(test6); TEST_CASE(test_numeric); 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; }) } 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() { // dont 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 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()); // #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()); } 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()); } }; REGISTER_TEST(TestIncompleteStatement) cppcheck-1.66/test/testinternal.cpp000066400000000000000000000340361236713773000174440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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" #include extern std::ostringstream errout; class TestInternal : public TestFixture { public: TestInternal() : TestFixture("TestInternal") { } private: void run() { 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(invalidMultiCompare); TEST_CASE(orInComplexPattern); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("internal"); // 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::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) Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::Match() call: \"foo|%type|bar\"\n" "[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()); } 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 invalidMultiCompare() { // #5310 check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \";|%type%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::Match() call: \";|%type%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \";|%oror%\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // The %var%|%num% works.. " const Token *tok;\n" " Token::Match(tok, \"%var%|%num%\");\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()); } }; REGISTER_TEST(TestInternal) #endif // #ifdef CHECK_INTERNAL cppcheck-1.66/test/testio.cpp000066400000000000000000011237151236713773000162430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "tokenize.h" #include extern std::ostringstream errout; class TestIO : public TestFixture { public: TestIO() : TestFixture("TestIO") { } private: void run() { 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); TEST_CASE(testScanf4); // #ticket 2553 TEST_CASE(testScanfArgument); TEST_CASE(testPrintfArgument); TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900 TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 TEST_CASE(testMicrosoftScanfArgument); TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920 TEST_CASE(testMicrosoftSecurePrintfArgument); TEST_CASE(testMicrosoftSecureScanfArgument); } void check(const char code[], bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); settings.addEnabled("style"); if (portability) settings.addEnabled("portability"); settings.inconclusive = inconclusive; settings.platform(platform); settings.library = _lib; // 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()); // 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()); // 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" "}"); 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", 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()); // #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()); } 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()); } 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 } void fflushOnInputStream() { check("void foo()\n" "{\n" " fflush(stdin);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) fflush() called on input stream 'stdin' results in undefined behaviour.\n", errout.str()); check("void foo()\n" "{\n" " fflush(stdout);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testScanf1() { LOAD_LIB("std.cfg"); 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) scanf 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) scanf 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() { LOAD_LIB("std.cfg"); 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() { check("void foo() {\n" " scanf(\"%d\", &a);\n" " scanf(\"%n\", &a);\n" // No warning on %n, since it doesn't expect user input " scanf(\"%c\", &c);\n" // No warning on %c; it expects only one character "}", false, true, Settings::Unspecified); ASSERT_EQUALS("[test.cpp:2]: (portability) scanf without field width limits can crash with huge input data on some versions of libc.\n", errout.str()); check("void foo() {\n" " scanf(\"%d\", &a);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("", errout.str()); } void testScanf4() { // ticket #2553 LOAD_LIB("std.cfg"); check("void f()\n" "{\n" " char str [8];\n" " scanf (\"%70s\",str);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Width 70 given in format string (no. 1) is larger than destination buffer 'str[8]', use %7s to prevent overflowing it.\n", errout.str()); } void testScanfArgument() { LOAD_LIB("std.cfg"); 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) scanf 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()); { const char * code = "const unsigned int * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 2) requires 'unsigned int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 4) requires 'unsigned int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 5) requires 'unsigned int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 6) requires 'unsigned int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 7) requires 'unsigned int *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 9) requires 'unsigned int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 10) requires 'unsigned int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 11) requires 'unsigned int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 12) requires 'unsigned int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 13) requires 'unsigned int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 14) requires 'unsigned int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 15) requires 'unsigned int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 16) requires 'unsigned int *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 17) requires 'unsigned int *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 18) requires 'unsigned int *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 19) requires 'unsigned int *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 20) requires 'unsigned int *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 21) requires 'unsigned int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 22) requires 'unsigned int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 23) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 24) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 25) requires 'unsigned int *' but the argument type is 'const unsigned int *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 28) requires 'unsigned int *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 29) requires 'unsigned int *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 30) requires 'unsigned int *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 31) requires 'unsigned int *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 32) requires 'unsigned int *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 2) requires 'unsigned int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 4) requires 'unsigned int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 5) requires 'unsigned int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 6) requires 'unsigned int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 7) requires 'unsigned int *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 9) requires 'unsigned int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 10) requires 'unsigned int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 11) requires 'unsigned int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 12) requires 'unsigned int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 13) requires 'unsigned int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 14) requires 'unsigned int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 15) requires 'unsigned int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 16) requires 'unsigned int *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 17) requires 'unsigned int *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 18) requires 'unsigned int *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 19) requires 'unsigned int *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 20) requires 'unsigned int *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 21) requires 'unsigned int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 22) requires 'unsigned int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 23) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 24) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 25) requires 'unsigned int *' but the argument type is 'const unsigned int *'.\n" "[test.cpp:29]: (warning) %u in format string (no. 28) requires 'unsigned int *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 29) requires 'unsigned int *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 30) requires 'unsigned int *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 31) requires 'unsigned int *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %u in format string (no. 32) requires 'unsigned int *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %lu in format string (no. 1) requires 'unsigned long *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 2) requires 'unsigned long *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 3) requires 'unsigned long *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 4) requires 'unsigned long *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 5) requires 'unsigned long *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 6) requires 'unsigned long *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 7) requires 'unsigned long *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 8) requires 'unsigned long *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 9) requires 'unsigned long *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 11) requires 'unsigned long *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 12) requires 'unsigned long *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 13) requires 'unsigned long *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 14) requires 'unsigned long *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 15) requires 'unsigned long *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 16) requires 'unsigned long *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 17) requires 'unsigned long *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 18) requires 'unsigned long *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 19) requires 'unsigned long *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 20) requires 'unsigned long *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 21) requires 'unsigned long *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 22) requires 'unsigned long *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 25) requires 'unsigned long *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 26) requires 'unsigned long *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 27) requires 'unsigned long *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 28) requires 'unsigned long *' but the argument type is 'std::intptr_t * {aka long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %lu in format string (no. 1) requires 'unsigned long *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 2) requires 'unsigned long *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 3) requires 'unsigned long *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 4) requires 'unsigned long *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 5) requires 'unsigned long *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 6) requires 'unsigned long *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 7) requires 'unsigned long *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 8) requires 'unsigned long *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 9) requires 'unsigned long *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 11) requires 'unsigned long *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 12) requires 'unsigned long *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 13) requires 'unsigned long *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 14) requires 'unsigned long *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 15) requires 'unsigned long *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 16) requires 'unsigned long *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 17) requires 'unsigned long *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 18) requires 'unsigned long *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 19) requires 'unsigned long *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 20) requires 'unsigned long *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 21) requires 'unsigned long *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 22) requires 'unsigned long *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 25) requires 'unsigned long *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 26) requires 'unsigned long *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 27) requires 'unsigned long *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 28) requires 'unsigned long *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %lu in format string (no. 29) requires 'unsigned long *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %llu in format string (no. 1) requires 'unsigned long long *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 2) requires 'unsigned long long *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 3) requires 'unsigned long long *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 4) requires 'unsigned long long *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 5) requires 'unsigned long long *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 6) requires 'unsigned long long *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 7) requires 'unsigned long long *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 8) requires 'unsigned long long *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 9) requires 'unsigned long long *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 10) requires 'unsigned long long *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 11) requires 'unsigned long long *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 13) requires 'unsigned long long *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 14) requires 'unsigned long long *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 15) requires 'unsigned long long *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 16) requires 'unsigned long long *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 17) requires 'unsigned long long *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 18) requires 'unsigned long long *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 19) requires 'unsigned long long *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 20) requires 'unsigned long long *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 21) requires 'unsigned long long *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 22) requires 'unsigned long long *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 25) requires 'unsigned long long *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 26) requires 'unsigned long long *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 27) requires 'unsigned long long *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 28) requires 'unsigned long long *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 29) requires 'unsigned long long *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %llu in format string (no. 1) requires 'unsigned long long *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 2) requires 'unsigned long long *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 3) requires 'unsigned long long *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 4) requires 'unsigned long long *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 5) requires 'unsigned long long *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 6) requires 'unsigned long long *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 7) requires 'unsigned long long *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 8) requires 'unsigned long long *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 9) requires 'unsigned long long *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 10) requires 'unsigned long long *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 11) requires 'unsigned long long *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 13) requires 'unsigned long long *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 14) requires 'unsigned long long *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 15) requires 'unsigned long long *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 16) requires 'unsigned long long *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 17) requires 'unsigned long long *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 18) requires 'unsigned long long *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 19) requires 'unsigned long long *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 20) requires 'unsigned long long *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 21) requires 'unsigned long long *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 22) requires 'unsigned long long *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 25) requires 'unsigned long long *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 26) requires 'unsigned long long *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 27) requires 'unsigned long long *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %llu in format string (no. 28) requires 'unsigned long long *' but the argument type is 'std::intptr_t * {aka long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu %hu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %hu in format string (no. 1) requires 'unsigned short *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 2) requires 'unsigned short *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 3) requires 'unsigned short *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 4) requires 'unsigned short *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 5) requires 'unsigned short *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 7) requires 'unsigned short *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 8) requires 'unsigned short *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 9) requires 'unsigned short *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 10) requires 'unsigned short *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 11) requires 'unsigned short *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 12) requires 'unsigned short *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 13) requires 'unsigned short *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 14) requires 'unsigned short *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 15) requires 'unsigned short *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 16) requires 'unsigned short *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 17) requires 'unsigned short *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 18) requires 'unsigned short *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 19) requires 'unsigned short *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 20) requires 'unsigned short *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 21) requires 'unsigned short *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 22) requires 'unsigned short *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 25) requires 'unsigned short *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 26) requires 'unsigned short *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 27) requires 'unsigned short *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 28) requires 'unsigned short *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 29) requires 'unsigned short *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %hu in format string (no. 1) requires 'unsigned short *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 2) requires 'unsigned short *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 3) requires 'unsigned short *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 4) requires 'unsigned short *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 5) requires 'unsigned short *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 7) requires 'unsigned short *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 8) requires 'unsigned short *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 9) requires 'unsigned short *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 10) requires 'unsigned short *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 11) requires 'unsigned short *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 12) requires 'unsigned short *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 13) requires 'unsigned short *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 14) requires 'unsigned short *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 15) requires 'unsigned short *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 16) requires 'unsigned short *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 17) requires 'unsigned short *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 18) requires 'unsigned short *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 19) requires 'unsigned short *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 20) requires 'unsigned short *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 21) requires 'unsigned short *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 22) requires 'unsigned short *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 25) requires 'unsigned short *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 26) requires 'unsigned short *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 27) requires 'unsigned short *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 28) requires 'unsigned short *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hu in format string (no. 29) requires 'unsigned short *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %hhu in format string (no. 1) requires 'unsigned char *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 2) requires 'unsigned char *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 3) requires 'unsigned char *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 5) requires 'unsigned char *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 6) requires 'unsigned char *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 7) requires 'unsigned char *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 8) requires 'unsigned char *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 9) requires 'unsigned char *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 10) requires 'unsigned char *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 11) requires 'unsigned char *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 12) requires 'unsigned char *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 13) requires 'unsigned char *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 14) requires 'unsigned char *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 15) requires 'unsigned char *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 16) requires 'unsigned char *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 17) requires 'unsigned char *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 18) requires 'unsigned char *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 19) requires 'unsigned char *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 20) requires 'unsigned char *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 21) requires 'unsigned char *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 22) requires 'unsigned char *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 25) requires 'unsigned char *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 26) requires 'unsigned char *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 27) requires 'unsigned char *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 28) requires 'unsigned char *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 29) requires 'unsigned char *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %hhu in format string (no. 1) requires 'unsigned char *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 2) requires 'unsigned char *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 3) requires 'unsigned char *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 5) requires 'unsigned char *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 6) requires 'unsigned char *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 7) requires 'unsigned char *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 8) requires 'unsigned char *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 9) requires 'unsigned char *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 10) requires 'unsigned char *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 11) requires 'unsigned char *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 12) requires 'unsigned char *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 13) requires 'unsigned char *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 14) requires 'unsigned char *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 15) requires 'unsigned char *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 16) requires 'unsigned char *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 17) requires 'unsigned char *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 18) requires 'unsigned char *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 19) requires 'unsigned char *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 20) requires 'unsigned char *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 21) requires 'unsigned char *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 22) requires 'unsigned char *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 25) requires 'unsigned char *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 26) requires 'unsigned char *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 27) requires 'unsigned char *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 28) requires 'unsigned char *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %hhu in format string (no. 29) requires 'unsigned char *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %Lu in format string (no. 1) requires 'unsigned long long *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 2) requires 'unsigned long long *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 3) requires 'unsigned long long *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 4) requires 'unsigned long long *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 5) requires 'unsigned long long *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 6) requires 'unsigned long long *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 7) requires 'unsigned long long *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 8) requires 'unsigned long long *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 9) requires 'unsigned long long *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 10) requires 'unsigned long long *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 11) requires 'unsigned long long *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 13) requires 'unsigned long long *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 14) requires 'unsigned long long *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 15) requires 'unsigned long long *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 16) requires 'unsigned long long *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 17) requires 'unsigned long long *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 18) requires 'unsigned long long *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 19) requires 'unsigned long long *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 20) requires 'unsigned long long *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 21) requires 'unsigned long long *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 22) requires 'unsigned long long *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 25) requires 'unsigned long long *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 26) requires 'unsigned long long *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 27) requires 'unsigned long long *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 28) requires 'unsigned long long *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 29) requires 'unsigned long long *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %Lu in format string (no. 1) requires 'unsigned long long *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 2) requires 'unsigned long long *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 3) requires 'unsigned long long *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 4) requires 'unsigned long long *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 5) requires 'unsigned long long *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 6) requires 'unsigned long long *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 7) requires 'unsigned long long *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 8) requires 'unsigned long long *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 9) requires 'unsigned long long *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 10) requires 'unsigned long long *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 11) requires 'unsigned long long *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 13) requires 'unsigned long long *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 14) requires 'unsigned long long *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 15) requires 'unsigned long long *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 16) requires 'unsigned long long *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 17) requires 'unsigned long long *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 18) requires 'unsigned long long *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 19) requires 'unsigned long long *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 20) requires 'unsigned long long *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 21) requires 'unsigned long long *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 22) requires 'unsigned long long *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 25) requires 'unsigned long long *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 26) requires 'unsigned long long *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 27) requires 'unsigned long long *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %Lu in format string (no. 28) requires 'unsigned long long *' but the argument type is 'std::intptr_t * {aka long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %ju in format string (no. 1) requires 'uintmax_t *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 2) requires 'uintmax_t *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 3) requires 'uintmax_t *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 4) requires 'uintmax_t *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 5) requires 'uintmax_t *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 6) requires 'uintmax_t *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 7) requires 'uintmax_t *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 8) requires 'uintmax_t *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 9) requires 'uintmax_t *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 10) requires 'uintmax_t *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 11) requires 'uintmax_t *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 12) requires 'uintmax_t *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 13) requires 'uintmax_t *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 14) requires 'uintmax_t *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 15) requires 'uintmax_t *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 16) requires 'uintmax_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 17) requires 'uintmax_t *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 18) requires 'uintmax_t *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 19) requires 'uintmax_t *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 21) requires 'uintmax_t *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 22) requires 'uintmax_t *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 25) requires 'uintmax_t *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 26) requires 'uintmax_t *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 27) requires 'uintmax_t *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 28) requires 'uintmax_t *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 29) requires 'uintmax_t *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %ju in format string (no. 1) requires 'uintmax_t *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 2) requires 'uintmax_t *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 3) requires 'uintmax_t *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 4) requires 'uintmax_t *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 5) requires 'uintmax_t *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 6) requires 'uintmax_t *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 7) requires 'uintmax_t *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 8) requires 'uintmax_t *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 9) requires 'uintmax_t *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 10) requires 'uintmax_t *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 11) requires 'uintmax_t *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 12) requires 'uintmax_t *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 13) requires 'uintmax_t *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 14) requires 'uintmax_t *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 15) requires 'uintmax_t *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 16) requires 'uintmax_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 17) requires 'uintmax_t *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 18) requires 'uintmax_t *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 19) requires 'uintmax_t *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 21) requires 'uintmax_t *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 22) requires 'uintmax_t *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 25) requires 'uintmax_t *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 26) requires 'uintmax_t *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 27) requires 'uintmax_t *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 28) requires 'uintmax_t *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %ju in format string (no. 29) requires 'uintmax_t *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:28]: (warning) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 2) requires 'size_t *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 3) requires 'size_t *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 4) requires 'size_t *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 5) requires 'size_t *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 6) requires 'size_t *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 7) requires 'size_t *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 8) requires 'size_t *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 9) requires 'size_t *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 10) requires 'size_t *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 11) requires 'size_t *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 12) requires 'size_t *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 13) requires 'size_t *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 14) requires 'size_t *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 15) requires 'size_t *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 17) requires 'size_t *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 18) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 19) requires 'size_t *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 20) requires 'size_t *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 21) requires 'size_t *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 22) requires 'size_t *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 26) requires 'size_t *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 27) requires 'size_t *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 28) requires 'size_t *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 29) requires 'size_t *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:28]: (warning) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 2) requires 'size_t *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 3) requires 'size_t *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 4) requires 'size_t *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 5) requires 'size_t *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 6) requires 'size_t *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 7) requires 'size_t *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 8) requires 'size_t *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 9) requires 'size_t *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 10) requires 'size_t *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 11) requires 'size_t *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 12) requires 'size_t *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 13) requires 'size_t *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 14) requires 'size_t *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 15) requires 'size_t *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 17) requires 'size_t *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 18) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 19) requires 'size_t *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 20) requires 'size_t *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 21) requires 'size_t *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 22) requires 'size_t *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 26) requires 'size_t *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 27) requires 'size_t *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 28) requires 'size_t *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:28]: (warning) %zu in format string (no. 29) requires 'size_t *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } check("void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu %tu\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n", false, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:28]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 2) requires 'unsigned ptrdiff_t *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 3) requires 'unsigned ptrdiff_t *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 4) requires 'unsigned ptrdiff_t *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 5) requires 'unsigned ptrdiff_t *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 6) requires 'unsigned ptrdiff_t *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 7) requires 'unsigned ptrdiff_t *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 8) requires 'unsigned ptrdiff_t *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 9) requires 'unsigned ptrdiff_t *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 10) requires 'unsigned ptrdiff_t *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 11) requires 'unsigned ptrdiff_t *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 12) requires 'unsigned ptrdiff_t *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 13) requires 'unsigned ptrdiff_t *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 14) requires 'unsigned ptrdiff_t *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 15) requires 'unsigned ptrdiff_t *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 16) requires 'unsigned ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 17) requires 'unsigned ptrdiff_t *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 18) requires 'unsigned ptrdiff_t *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 19) requires 'unsigned ptrdiff_t *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 20) requires 'unsigned ptrdiff_t *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 21) requires 'unsigned ptrdiff_t *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 22) requires 'unsigned ptrdiff_t *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 25) requires 'unsigned ptrdiff_t *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 26) requires 'unsigned ptrdiff_t *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 27) requires 'unsigned ptrdiff_t *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 28) requires 'unsigned ptrdiff_t *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %tu in format string (no. 29) requires 'unsigned ptrdiff_t *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n", errout.str()); check("void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u %I64u\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n", false, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:28]: (warning) %I64u in format string (no. 1) requires 'unsigned __int64 *' but the argument type is 'bool *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is 'char *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 3) requires 'unsigned __int64 *' but the argument type is 'signed char *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64 *' but the argument type is 'unsigned char *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64 *' but the argument type is 'short *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 6) requires 'unsigned __int64 *' but the argument type is 'unsigned short *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64 *' but the argument type is 'int *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 8) requires 'unsigned __int64 *' but the argument type is 'unsigned int *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 9) requires 'unsigned __int64 *' but the argument type is 'long *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 10) requires 'unsigned __int64 *' but the argument type is 'unsigned long *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 11) requires 'unsigned __int64 *' but the argument type is 'long long *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 13) requires 'unsigned __int64 *' but the argument type is 'float *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 14) requires 'unsigned __int64 *' but the argument type is 'double *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 15) requires 'unsigned __int64 *' but the argument type is 'long double *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 16) requires 'unsigned __int64 *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 17) requires 'unsigned __int64 *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 18) requires 'unsigned __int64 *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 19) requires 'unsigned __int64 *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 20) requires 'unsigned __int64 *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 21) requires 'unsigned __int64 *' but the argument type is 'void * *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 22) requires 'unsigned __int64 *' but the argument type is 'void *'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 25) requires 'unsigned __int64 *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 26) requires 'unsigned __int64 *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 27) requires 'unsigned __int64 *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 28) requires 'unsigned __int64 *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:28]: (warning) %I64u in format string (no. 29) requires 'unsigned __int64 *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n", errout.str()); { const char * code = "const int * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 3) requires 'int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 4) requires 'int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 5) requires 'int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 6) requires 'int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 8) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 9) requires 'int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 10) requires 'int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 11) requires 'int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 12) requires 'int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 13) requires 'int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 14) requires 'int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 15) requires 'int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 16) requires 'int *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 17) requires 'int *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 18) requires 'int *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 19) requires 'int *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 20) requires 'int *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 21) requires 'int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 22) requires 'int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 23) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 24) requires 'int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 25) requires 'int *' but the argument type is 'const int *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 28) requires 'int *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 29) requires 'int *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 30) requires 'int *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 31) requires 'int *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 32) requires 'int *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 3) requires 'int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 4) requires 'int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 5) requires 'int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 6) requires 'int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 8) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 9) requires 'int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 10) requires 'int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 11) requires 'int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 12) requires 'int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 13) requires 'int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 14) requires 'int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 15) requires 'int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 16) requires 'int *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 17) requires 'int *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 18) requires 'int *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 19) requires 'int *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 20) requires 'int *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 21) requires 'int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 22) requires 'int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 23) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 24) requires 'int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 25) requires 'int *' but the argument type is 'const int *'.\n" "[test.cpp:29]: (warning) %d in format string (no. 28) requires 'int *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 29) requires 'int *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 30) requires 'int *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 31) requires 'int *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %d in format string (no. 32) requires 'int *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "const unsigned int * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 2) requires 'unsigned int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 4) requires 'unsigned int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 5) requires 'unsigned int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 6) requires 'unsigned int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 9) requires 'unsigned int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 10) requires 'unsigned int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 11) requires 'unsigned int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 12) requires 'unsigned int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 13) requires 'unsigned int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 14) requires 'unsigned int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 15) requires 'unsigned int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 16) requires 'unsigned int *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 17) requires 'unsigned int *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 18) requires 'unsigned int *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 19) requires 'unsigned int *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 20) requires 'unsigned int *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 21) requires 'unsigned int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 22) requires 'unsigned int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 23) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 24) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 25) requires 'unsigned int *' but the argument type is 'const unsigned int *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 28) requires 'unsigned int *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 29) requires 'unsigned int *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 30) requires 'unsigned int *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 31) requires 'unsigned int *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 32) requires 'unsigned int *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 2) requires 'unsigned int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 4) requires 'unsigned int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 5) requires 'unsigned int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 6) requires 'unsigned int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 9) requires 'unsigned int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 10) requires 'unsigned int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 11) requires 'unsigned int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 12) requires 'unsigned int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 13) requires 'unsigned int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 14) requires 'unsigned int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 15) requires 'unsigned int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 16) requires 'unsigned int *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 17) requires 'unsigned int *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 18) requires 'unsigned int *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 19) requires 'unsigned int *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 20) requires 'unsigned int *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 21) requires 'unsigned int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 22) requires 'unsigned int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 23) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 24) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 25) requires 'unsigned int *' but the argument type is 'const unsigned int *'.\n" "[test.cpp:29]: (warning) %x in format string (no. 28) requires 'unsigned int *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 29) requires 'unsigned int *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 30) requires 'unsigned int *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 31) requires 'unsigned int *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %x in format string (no. 32) requires 'unsigned int *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "const float * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 2) requires 'float *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 3) requires 'float *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 4) requires 'float *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 5) requires 'float *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 6) requires 'float *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 7) requires 'float *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 8) requires 'float *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 9) requires 'float *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 10) requires 'float *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 11) requires 'float *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 12) requires 'float *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 14) requires 'float *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 15) requires 'float *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 16) requires 'float *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 17) requires 'float *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 18) requires 'float *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 19) requires 'float *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 20) requires 'float *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 21) requires 'float *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 22) requires 'float *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 23) requires 'float *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 24) requires 'float *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 25) requires 'float *' but the argument type is 'const float *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 28) requires 'float *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 29) requires 'float *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 30) requires 'float *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 31) requires 'float *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 32) requires 'float *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 2) requires 'float *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 3) requires 'float *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 4) requires 'float *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 5) requires 'float *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 6) requires 'float *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 7) requires 'float *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 8) requires 'float *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 9) requires 'float *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 10) requires 'float *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 11) requires 'float *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 12) requires 'float *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 14) requires 'float *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 15) requires 'float *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 16) requires 'float *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 17) requires 'float *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 18) requires 'float *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 19) requires 'float *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 20) requires 'float *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 21) requires 'float *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 22) requires 'float *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 23) requires 'float *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 24) requires 'float *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 25) requires 'float *' but the argument type is 'const float *'.\n" "[test.cpp:29]: (warning) %f in format string (no. 28) requires 'float *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 29) requires 'float *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 30) requires 'float *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 31) requires 'float *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %f in format string (no. 32) requires 'float *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, true, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, true, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "const double * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %lf in format string (no. 1) requires 'double *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 2) requires 'double *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 3) requires 'double *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 4) requires 'double *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 5) requires 'double *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 6) requires 'double *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 7) requires 'double *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 8) requires 'double *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 9) requires 'double *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 10) requires 'double *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 11) requires 'double *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 12) requires 'double *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 13) requires 'double *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 15) requires 'double *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 16) requires 'double *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 17) requires 'double *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 18) requires 'double *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 19) requires 'double *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 20) requires 'double *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 21) requires 'double *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 22) requires 'double *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 23) requires 'double *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 24) requires 'double *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 25) requires 'double *' but the argument type is 'const double *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 28) requires 'double *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 29) requires 'double *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 30) requires 'double *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 31) requires 'double *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 32) requires 'double *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %lf in format string (no. 1) requires 'double *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 2) requires 'double *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 3) requires 'double *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 4) requires 'double *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 5) requires 'double *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 6) requires 'double *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 7) requires 'double *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 8) requires 'double *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 9) requires 'double *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 10) requires 'double *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 11) requires 'double *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 12) requires 'double *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 13) requires 'double *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 15) requires 'double *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 16) requires 'double *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 17) requires 'double *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 18) requires 'double *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 19) requires 'double *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 20) requires 'double *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 21) requires 'double *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 22) requires 'double *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 23) requires 'double *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 24) requires 'double *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 25) requires 'double *' but the argument type is 'const double *'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 28) requires 'double *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 29) requires 'double *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 30) requires 'double *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 31) requires 'double *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %lf in format string (no. 32) requires 'double *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, false, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "const long double * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf %Lf\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %Lf in format string (no. 1) requires 'long double *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 2) requires 'long double *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 3) requires 'long double *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 4) requires 'long double *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 5) requires 'long double *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 6) requires 'long double *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 7) requires 'long double *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 8) requires 'long double *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 9) requires 'long double *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 10) requires 'long double *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 11) requires 'long double *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 12) requires 'long double *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 13) requires 'long double *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 14) requires 'long double *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 16) requires 'long double *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 17) requires 'long double *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 18) requires 'long double *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 19) requires 'long double *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 20) requires 'long double *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 21) requires 'long double *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 22) requires 'long double *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 23) requires 'long double *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 24) requires 'long double *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 25) requires 'long double *' but the argument type is 'const long double *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 28) requires 'long double *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 29) requires 'long double *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 30) requires 'long double *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 31) requires 'long double *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 32) requires 'long double *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %Lf in format string (no. 1) requires 'long double *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 2) requires 'long double *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 3) requires 'long double *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 4) requires 'long double *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 5) requires 'long double *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 6) requires 'long double *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 7) requires 'long double *' but the argument type is 'int *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 8) requires 'long double *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 9) requires 'long double *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 10) requires 'long double *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 11) requires 'long double *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 12) requires 'long double *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 13) requires 'long double *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 14) requires 'long double *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 16) requires 'long double *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 17) requires 'long double *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 18) requires 'long double *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 19) requires 'long double *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 20) requires 'long double *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 21) requires 'long double *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 22) requires 'long double *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 23) requires 'long double *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 24) requires 'long double *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 25) requires 'long double *' but the argument type is 'const long double *'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 28) requires 'long double *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 29) requires 'long double *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 30) requires 'long double *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 31) requires 'long double *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %Lf in format string (no. 32) requires 'long double *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, false, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { const char * code = "const int * foo() { }\n" "void foo() {\n" " bool b;\n" " char c;\n" " signed char sc;\n" " unsigned char uc;\n" " short s;\n" " unsigned short us;\n" " int i;\n" " unsigned int ui;\n" " long l;\n" " unsigned long ul;\n" " long long ll;\n" " unsigned long long ull;\n" " float f;\n" " double d;\n" " long double ld;\n" " size_t st;\n" " ssize_t sst;\n" " ptrdiff_t pt;\n" " intmax_t it;\n" " uintmax_t ut;\n" " void * vp;\n" " std::size_t stdst;\n" " std::ssize_t stdsst;\n" " std::ptrdiff_t stdpt;\n" " std::intptr_t stdipt;\n" " std::uintptr_t stduipt;\n" " scanf(\"%n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n %n\",\n" " &b, &c, &sc, &uc, &s, &us, &i, &ui, &l, &ul, &ll, &ull, &f, &d, &ld, &st, &sst, &pt, &it, &ut, &vp, vp,\n" " \"str\", L\"str\", foo(), &unknown, unknown, &stdst, &stdsst, &stdpt, &stdipt, &stduipt);\n" "}\n"; std::string result("[test.cpp:29]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 2) requires 'int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 3) requires 'int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 4) requires 'int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 5) requires 'int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 6) requires 'int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 8) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 9) requires 'int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 10) requires 'int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 11) requires 'int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 12) requires 'int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 13) requires 'int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 14) requires 'int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 15) requires 'int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 16) requires 'int *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 17) requires 'int *' but the argument type is 'ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 18) requires 'int *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 19) requires 'int *' but the argument type is 'intmax_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 20) requires 'int *' but the argument type is 'uintmax_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 21) requires 'int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 22) requires 'int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 23) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 24) requires 'int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 25) requires 'int *' but the argument type is 'const int *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 28) requires 'int *' but the argument type is 'std::size_t * {aka unsigned long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 29) requires 'int *' but the argument type is 'std::ssize_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 30) requires 'int *' but the argument type is 'std::ptrdiff_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 31) requires 'int *' but the argument type is 'std::intptr_t * {aka long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 32) requires 'int *' but the argument type is 'std::uintptr_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:29]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'bool *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 2) requires 'int *' but the argument type is 'char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 3) requires 'int *' but the argument type is 'signed char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 4) requires 'int *' but the argument type is 'unsigned char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 5) requires 'int *' but the argument type is 'short *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 6) requires 'int *' but the argument type is 'unsigned short *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 8) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 9) requires 'int *' but the argument type is 'long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 10) requires 'int *' but the argument type is 'unsigned long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 11) requires 'int *' but the argument type is 'long long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 12) requires 'int *' but the argument type is 'unsigned long long *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 13) requires 'int *' but the argument type is 'float *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 14) requires 'int *' but the argument type is 'double *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 15) requires 'int *' but the argument type is 'long double *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 16) requires 'int *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 17) requires 'int *' but the argument type is 'ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 18) requires 'int *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 19) requires 'int *' but the argument type is 'intmax_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 20) requires 'int *' but the argument type is 'uintmax_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 21) requires 'int *' but the argument type is 'void * *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 22) requires 'int *' but the argument type is 'void *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 23) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 24) requires 'int *' but the argument type is 'const wchar_t *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 25) requires 'int *' but the argument type is 'const int *'.\n" "[test.cpp:29]: (warning) %n in format string (no. 28) requires 'int *' but the argument type is 'std::size_t * {aka unsigned long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 29) requires 'int *' but the argument type is 'std::ssize_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 30) requires 'int *' but the argument type is 'std::ptrdiff_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 31) requires 'int *' but the argument type is 'std::intptr_t * {aka long long *}'.\n" "[test.cpp:29]: (warning) %n in format string (no. 32) requires 'int *' but the argument type is 'std::uintptr_t * {aka unsigned long long *}'.\n"); check(code, false, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } check("void g() {\n" // #5104 " myvector v1(1);\n" " scanf(\"%d\n\",&v1[0]);\n" " myvector v2(1);\n" " scanf(\"%u\n\",&v2[0]);\n" " myvector v3(1);\n" " scanf(\"%x\n\",&v3[0]);\n" " myvector v4(1);\n" " scanf(\"%lf\n\",&v4[0]);\n" " myvector v5(1);\n" " scanf(\"%10s\n\",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"; std::string result("[test.cpp:5]: (warning) %zd in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n"); std::string result_win64("[test.cpp:5]: (warning) %zd in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n"); check(code, false, false, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, false, false, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { check("void g() {\n" " const char c[]=\"42\";\n" " scanf(\"%s\n\", 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() { LOAD_LIB("std.cfg"); check("void foo() {\n" " printf(\"%u\");\n" " printf(\"%u%s\", 123);\n" " printf(\"%u%s%d\", 0, bar());\n" " printf(\"%u%%%s%d\", 0, bar());\n" " printf(\"%udfd%%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(\"%u\", 123, bar());\n" " printf(\"%u%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\"%u\", 32, string2);\n" // MSVC implementation " swprintf(string1, L\"%s%s\", L\"a\", string2);\n" // MSVC implementation " swprintf(string1, 6, L\"%u\", 32, string2);\n" // Standard implementation " swprintf(string1, 6, L\"%u%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(\"%u\", 0);\n" " printf(\"%u%s\", 123, bar());\n" " printf(\"%u%s%d\", 0, bar(), 43123);\n" " printf(\"%u%%%s%d\", 0, bar(), 43123);\n" " printf(\"%udfd%%dfa%s%d\", 0, bar(), 43123);\n" " printf(\"%\"PRId64\"\n\", 123);\n" " fprintf(stderr,\"%\"PRId64\"\n\", 123);\n" " snprintf(str,10,\"%\"PRId64\"\n\", 123);\n" " fprintf(stderr, \"error: %m\n\");\n" // #3339 " printf(\"string: %.*s\n\", len, string);\n" // #3311 " fprintf(stderr, \"%*cText.\n\", 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 'int'.\n" "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is '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(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:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const int *'.\n" "[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const int'.\n" "[test.cpp:4]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is '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("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 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 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 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(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 'int'.\n" "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const 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, 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(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 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(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("", 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(\"%La\", ld);\n" " printf(\"%zx\", ss);\n" " printf(\"%ti\", sp);\n" "}"); ASSERT_EQUALS("", 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" "}"); 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", 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 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:3]: (warning) %hhd in format string (no. 1) requires 'int' 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, false, Settings::Unix32); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka 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, false, Settings::Unix32); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka 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, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka 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, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, false, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (warning) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka 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, false, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (warning) %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]: (warning) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka 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("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 '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 'short'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'short'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'short'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'short'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'short'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'short'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is '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 'int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is '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 'long'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is '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 'long long'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'long long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'long long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'long long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'long long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is '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("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 'int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is '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 'int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is '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("", 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 'int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is '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 'int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is '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 'int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is '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 'int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is '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 'int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is '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 'int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is '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("", 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 '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 '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, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:4]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %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, false, Settings::Win64); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:4]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (warning) %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, false, Settings::Unix32); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:4]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %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, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:4]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %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, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:4]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (warning) %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, false, 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, false, 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, false, 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]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:9]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:10]: (warning) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:10]: (warning) %lu in format string (no. 2) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:11]: (warning) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:11]: (warning) %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(){ return 0; }\n" "char c; char cf(){ return 0; }\n" "signed char sc; signed char scf(){ return 0; }\n" "unsigned char uc; unsigned char ucf(){ return 0; }\n" "short s; short sf(){ return 0; }\n" "unsigned short us; unsigned short usf(){ return 0; }\n" "size_t st; size_t stf(){ return 0; }\n" "ptrdiff_t pt; ptrdiff_t ptf(){ return 0; }\n" "char * pc; char * pcf(){ return 0; }\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" " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\", b, c, sc, uc, s, us, st, pt, pc, cl, ca);\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" " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld\", bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" "}\n", false, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (warning) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka long}'.\n" "[test.cpp:13]: (warning) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (warning) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka 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" "[test.cpp:14]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'short'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka long}'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 10) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:14]: (warning) %ld in format string (no. 11) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:15]: (warning) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka long}'.\n" "[test.cpp:15]: (warning) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:15]: (warning) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka long}'.\n" "[test.cpp:15]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'short'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:16]: (warning) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka long}'.\n" "[test.cpp:16]: (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, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:6]: (warning) %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, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:7]: (warning) %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, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'DWORD {aka unsigned long}'.\n" "[test.cpp:4]: (warning) %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 '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 'long'.\n" "[test.cpp:3]: (warning) %x in format string (no. 2) requires 'unsigned int' but the argument type is 'long'.\n" "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'long'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'long'.\n", errout.str()); check("void f() {\n" // #5104 " myvector v1(1,0);\n" " printf(\"%d\n\",v1[0]);\n" " myvector v2(1,0);\n" " printf(\"%d\n\",v2[0]);\n" " myvector v3(1,0);\n" " printf(\"%u\n\",v3[0]);\n" " myvector v4(1,0);\n" " printf(\"%x\n\",v4[0]);\n" " myvector v5(1,0);\n" " printf(\"%f\n\",v5[0]);\n" " myvector v6(1,0);\n" " printf(\"%u\n\",v6[0]);\n" " myvector v7(1,0);\n" " printf(\"%s\n\",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 'int'.\n", errout.str()); } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings LOAD_LIB("std.cfg"); 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() { LOAD_LIB("std.cfg"); LOAD_LIB("windows.cfg"); 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, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:9]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka long}'.\n" "[test.cpp:10]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka int}'.\n" "[test.cpp:11]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:12]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka long long}'.\n" "[test.cpp:13]: (warning) %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, false, Settings::Win64); ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:9]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka long long}'.\n" "[test.cpp:10]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka int}'.\n" "[test.cpp:11]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:12]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka long long}'.\n" "[test.cpp:13]: (warning) %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, false, 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, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); } void testMicrosoftScanfArgument() { LOAD_LIB("std.cfg"); LOAD_LIB("windows.cfg"); 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, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:9]: (warning) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka long *}'.\n" "[test.cpp:10]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka int *}'.\n" "[test.cpp:11]: (warning) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" "[test.cpp:12]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka long long *}'.\n" "[test.cpp:13]: (warning) %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, false, Settings::Win64); ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:9]: (warning) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n" "[test.cpp:10]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka int *}'.\n" "[test.cpp:11]: (warning) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" "[test.cpp:12]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka long long *}'.\n" "[test.cpp:13]: (warning) %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, false, 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, false, 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" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:5]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n", errout.str()); } void testMicrosoftSecurePrintfArgument() { LOAD_LIB("std.cfg"); LOAD_LIB("windows.cfg"); 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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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 '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\";\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\";\n" " const char format2[] = \"%15s%17s%17s%17s%17s\n\";\n" " const char * const format3 = format1;\n" // we should warn about this someday " 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" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is '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 'int'.\n" "[test.cpp:7]: (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 '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 'int'.\n" "[test.cpp:10]: (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 '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 'int'.\n" "[test.cpp:13]: (warning) printf format string requires 5 parameters but 6 are given.\n", errout.str()); } void testMicrosoftSecureScanfArgument() { LOAD_LIB("windows.cfg"); 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 '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 '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 '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 'int *'.\n" "[test.cpp:5]: (warning) wscanf_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::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 '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 '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 '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 '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 '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 '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 '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 '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()); } }; REGISTER_TEST(TestIO) cppcheck-1.66/test/testleakautovar.cpp000066400000000000000000000521001236713773000201360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkleakautovar.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestLeakAutoVar : public TestFixture { public: TestLeakAutoVar() : TestFixture("TestLeakAutoVar") { } private: void run() { // 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(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(doublefree1); TEST_CASE(doublefree2); TEST_CASE(doublefree3); // #4914 TEST_CASE(doublefree4); // #5451 - FP when exit is called // exit TEST_CASE(exit1); TEST_CASE(exit2); // 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) // switch TEST_CASE(switch1); // loops TEST_CASE(loop1); // mismatching allocation/deallocation TEST_CASE(mismatch_fopen_free); // Execution reaches a 'return' TEST_CASE(return1); TEST_CASE(return2); TEST_CASE(return3); TEST_CASE(return4); // 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 // Execution reaches a 'throw' TEST_CASE(throw1); // Possible leak => Further configuration is needed for complete analysis TEST_CASE(configuration1); TEST_CASE(configuration2); TEST_CASE(configuration3); TEST_CASE(configuration4); TEST_CASE(ptrptr); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Settings settings; int id = 0; while (!settings.library.ismemory(++id)); settings.library.setalloc("malloc",id); settings.library.setdealloc("free",id); while (!settings.library.isresource(++id)); settings.library.setalloc("fopen",id); settings.library.setdealloc("fclose",id); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "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 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()); } 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 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()); } 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 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 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("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 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 mismatch_fopen_free() { 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()); } 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" "}"); 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" "}"); 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" "}"); 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 throw1() { // 3987 - Execution reach a 'throw' check("void f() {\n" " char *p = malloc(10);\n" " throw 123;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " char *p;\n" " try {\n" " p = malloc(10);\n" " throw 123;\n" " } catch (...) { }\n" " free(p);\n" "}"); 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()); } }; REGISTER_TEST(TestLeakAutoVar) cppcheck-1.66/test/testlibrary.cpp000066400000000000000000000257101236713773000172730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "token.h" #include "tokenlist.h" #include "testsuite.h" #include class TestLibrary : public TestFixture { public: TestLibrary() : TestFixture("TestLibrary") { } private: void run() { TEST_CASE(empty); TEST_CASE(function); TEST_CASE(function_arg); TEST_CASE(function_arg_any); TEST_CASE(function_arg_valid); TEST_CASE(function_arg_minsize); TEST_CASE(memory); TEST_CASE(memory2); // define extra "free" allocation functions TEST_CASE(resource); TEST_CASE(podtype); } void empty() const { const char xmldata[] = "\n"; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); ASSERT(library.use.empty()); ASSERT(library.leakignore.empty()); ASSERT(library.argumentChecks.empty()); } void function() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); ASSERT(library.use.empty()); ASSERT(library.leakignore.empty()); ASSERT(library.argumentChecks.empty()); ASSERT(library.isnotnoreturn("foo")); } void function_arg() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); ASSERT_EQUALS(true, library.argumentChecks["foo"][1].notuninit); ASSERT_EQUALS(true, library.argumentChecks["foo"][2].notnull); ASSERT_EQUALS(true, library.argumentChecks["foo"][3].formatstr); ASSERT_EQUALS(true, library.argumentChecks["foo"][4].strz); ASSERT_EQUALS(true, library.argumentChecks["foo"][5].notbool); } void function_arg_any() const { const char xmldata[] = "\n" "\n" "\n" " \n" "\n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); ASSERT_EQUALS(true, library.argumentChecks["foo"][-1].notuninit); } 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" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); // 1- ASSERT_EQUALS(false, library.isargvalid("foo", 1, -10)); ASSERT_EQUALS(false, library.isargvalid("foo", 1, 0)); ASSERT_EQUALS(true, library.isargvalid("foo", 1, 1)); ASSERT_EQUALS(true, library.isargvalid("foo", 1, 10)); // -7-0 ASSERT_EQUALS(false, library.isargvalid("foo", 2, -10)); ASSERT_EQUALS(true, library.isargvalid("foo", 2, -7)); ASSERT_EQUALS(true, library.isargvalid("foo", 2, -3)); ASSERT_EQUALS(true, library.isargvalid("foo", 2, 0)); ASSERT_EQUALS(false, library.isargvalid("foo", 2, 1)); // 1-5,8 ASSERT_EQUALS(false, library.isargvalid("foo", 3, 0)); ASSERT_EQUALS(true, library.isargvalid("foo", 3, 1)); ASSERT_EQUALS(true, library.isargvalid("foo", 3, 3)); ASSERT_EQUALS(true, library.isargvalid("foo", 3, 5)); ASSERT_EQUALS(false, library.isargvalid("foo", 3, 6)); ASSERT_EQUALS(false, library.isargvalid("foo", 3, 7)); ASSERT_EQUALS(true, library.isargvalid("foo", 3, 8)); ASSERT_EQUALS(false, library.isargvalid("foo", 3, 9)); // -1,5 ASSERT_EQUALS(false, library.isargvalid("foo", 4, -10)); ASSERT_EQUALS(true, library.isargvalid("foo", 4, -1)); // :1,5 ASSERT_EQUALS(true, library.isargvalid("foo", 5, -10)); ASSERT_EQUALS(true, library.isargvalid("foo", 5, 1)); ASSERT_EQUALS(false, library.isargvalid("foo", 5, 2)); } void function_arg_minsize() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); // arg1: type=strlen arg2 const std::list *minsizes = library.argminsizes("foo",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("foo", 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 memory() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); ASSERT(library.use.empty()); ASSERT(library.leakignore.empty()); ASSERT(library.argumentChecks.empty()); ASSERT(Library::ismemory(library.alloc("CreateX"))); ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX")); } 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.dealloc("free"), library.alloc("malloc")); ASSERT_EQUALS(library.dealloc("free"), library.alloc("foo")); } void resource() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); ASSERT(library.use.empty()); ASSERT(library.leakignore.empty()); ASSERT(library.argumentChecks.empty()); ASSERT(Library::isresource(library.alloc("CreateX"))); ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX")); } void podtype() const { const char xmldata[] = "\n" "\n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); Library library; library.load(doc); const struct Library::PodType *type = library.podtype("s16"); ASSERT_EQUALS(2U, type ? type->size : 0U); ASSERT_EQUALS(0, type ? type->sign : '?'); } }; REGISTER_TEST(TestLibrary) cppcheck-1.66/test/testmathlib.cpp000066400000000000000000001176221236713773000172530ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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" class TestMathLib : public TestFixture { public: TestMathLib() : TestFixture("TestMathLib") { } private: void run() { TEST_CASE(isint); TEST_CASE(isbin); TEST_CASE(isoct); TEST_CASE(ishex); TEST_CASE(isnegative); TEST_CASE(ispositive); TEST_CASE(isfloat); 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(convert); TEST_CASE(naninf); TEST_CASE(isNullValue); TEST_CASE(incdec); } 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 MathLib::divide("123", "0.0"); // don't throw // Unknown action should throw exception ASSERT_THROW(MathLib::calculate("1","2",'j'),InternalError); } void calculate1() const { // mod ASSERT_EQUALS("0" , MathLib::calculate("2" , "1" , '%')); ASSERT_EQUALS("0.0" , MathLib::calculate("2.0" , "1.0" , '%')); ASSERT_EQUALS("2" , MathLib::calculate("12" , "5" , '%')); ASSERT_EQUALS("1" , MathLib::calculate("100" , "3" , '%')); 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" , '%')); ASSERT_THROW(MathLib::calculate("123", "0", '%'), InternalError); // throw MathLib::calculate("123", "0.0", '%'); // don't throw } void convert() const { // ------------------ // tolong conversion: // ------------------ // from hex 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 long long ASSERT_EQUALS(0xFF00000000000000LL, MathLib::toLongNumber("0xFF00000000000000LL")); ASSERT_EQUALS(0x0A00000000000000LL, MathLib::toLongNumber("0x0A00000000000000LL")); // ----------------- // to double number: // ----------------- ASSERT_EQUALS_DOUBLE(10.0 , MathLib::toDoubleNumber("10")); ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2")); ASSERT_EQUALS_DOUBLE(100.0 , MathLib::toDoubleNumber("1.0E+2")); ASSERT_EQUALS_DOUBLE(-100.0, MathLib::toDoubleNumber("-1.0E+2")); ASSERT_EQUALS_DOUBLE(-1e+10, MathLib::toDoubleNumber("-1.0E+10")); ASSERT_EQUALS_DOUBLE(100.0 , MathLib::toDoubleNumber("+1.0E+2")); ASSERT_EQUALS_DOUBLE(1e+10 , MathLib::toDoubleNumber("+1.0E+10")); ASSERT_EQUALS_DOUBLE(100.0 , MathLib::toDoubleNumber("1.0E+2")); ASSERT_EQUALS_DOUBLE(1e+10 , MathLib::toDoubleNumber("1.0E+10")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0E+0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0E-0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0E+00")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0E-00")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("-0E+00")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("+0E-00")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0.")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("0.0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("-0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("+0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("-0.")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("+0.")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("-0.0")); ASSERT_EQUALS_DOUBLE(0.0 , MathLib::toDoubleNumber("+0.0")); // 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"))); } 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("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 ishex() const { // hex number syntax: [sign]0x[hexnumbers][suffix] // positive testing ASSERT_EQUALS(true, MathLib::isHex("0xa")); ASSERT_EQUALS(true, MathLib::isHex("0x2AF3")); ASSERT_EQUALS(true, MathLib::isHex("-0xa")); ASSERT_EQUALS(true, MathLib::isHex("-0x2AF3")); ASSERT_EQUALS(true, MathLib::isHex("+0xa")); ASSERT_EQUALS(true, MathLib::isHex("+0x2AF3")); ASSERT_EQUALS(true, MathLib::isHex("0x0")); ASSERT_EQUALS(true, MathLib::isHex("+0x0")); ASSERT_EQUALS(true, MathLib::isHex("-0x0")); ASSERT_EQUALS(true, MathLib::isHex("+0x0U")); ASSERT_EQUALS(true, MathLib::isHex("-0x0U")); ASSERT_EQUALS(true, MathLib::isHex("+0x0L")); ASSERT_EQUALS(true, MathLib::isHex("-0x0L")); ASSERT_EQUALS(true, MathLib::isHex("+0x0LU")); ASSERT_EQUALS(true, MathLib::isHex("-0x0LU")); ASSERT_EQUALS(true, MathLib::isHex("+0x0UL")); ASSERT_EQUALS(true, MathLib::isHex("-0x0UL")); ASSERT_EQUALS(true, MathLib::isHex("+0x0LL")); ASSERT_EQUALS(true, MathLib::isHex("-0x0LL")); ASSERT_EQUALS(true, MathLib::isHex("+0x0ULL")); ASSERT_EQUALS(true, MathLib::isHex("-0x0ULL")); ASSERT_EQUALS(true, MathLib::isHex("+0x0LLU")); ASSERT_EQUALS(true, MathLib::isHex("-0x0LLU")); // negative testing ASSERT_EQUALS(false, MathLib::isHex("+0x")); ASSERT_EQUALS(false, MathLib::isHex("-0x")); ASSERT_EQUALS(false, MathLib::isHex("0x")); ASSERT_EQUALS(false, MathLib::isHex("0xx")); ASSERT_EQUALS(false, MathLib::isHex("-0175")); ASSERT_EQUALS(false, MathLib::isHex("-0_garbage_")); ASSERT_EQUALS(false, MathLib::isHex(" ")); ASSERT_EQUALS(false, MathLib::isHex(" ")); ASSERT_EQUALS(false, MathLib::isHex("0")); ASSERT_EQUALS(false, MathLib::isHex("+0x0Z")); ASSERT_EQUALS(false, MathLib::isHex("-0x0Z")); ASSERT_EQUALS(false, MathLib::isHex("+0x0Uz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0Uz")); ASSERT_EQUALS(false, MathLib::isHex("+0x0Lz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0Lz")); ASSERT_EQUALS(false, MathLib::isHex("+0x0LUz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0LUz")); ASSERT_EQUALS(false, MathLib::isHex("+0x0ULz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0ULz")); ASSERT_EQUALS(false, MathLib::isHex("+0x0LLz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0LLz")); ASSERT_EQUALS(false, MathLib::isHex("+0x0ULLz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0ULLz")); ASSERT_EQUALS(false, MathLib::isHex("+0x0LLUz")); ASSERT_EQUALS(false, MathLib::isHex("-0x0LLUz")); ASSERT_EQUALS(false, MathLib::isHex("0x0+0")); ASSERT_EQUALS(false, MathLib::isHex("e2")); ASSERT_EQUALS(false, MathLib::isHex("+E2")); // test empty string ASSERT_EQUALS(false, MathLib::isHex("")); } 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(true, MathLib::isPositive("")); // because it has opposite result to MathLib::isNegative } void isfloat() const { ASSERT_EQUALS(false, MathLib::isFloat("")); ASSERT_EQUALS(false, MathLib::isFloat(".")); ASSERT_EQUALS(false, MathLib::isFloat("...")); ASSERT_EQUALS(false, MathLib::isFloat("+E.")); ASSERT_EQUALS(false, MathLib::isFloat("+e.")); ASSERT_EQUALS(false, MathLib::isFloat("-E.")); ASSERT_EQUALS(false, MathLib::isFloat("-e.")); ASSERT_EQUALS(false, MathLib::isFloat("-.")); ASSERT_EQUALS(false, MathLib::isFloat("-.")); ASSERT_EQUALS(false, MathLib::isFloat("-")); ASSERT_EQUALS(false, MathLib::isFloat("+")); ASSERT_EQUALS(false, MathLib::isFloat(" ")); ASSERT_EQUALS(false, MathLib::isFloat("0")); ASSERT_EQUALS(false, MathLib::isFloat("0 ")); ASSERT_EQUALS(false, MathLib::isFloat(" 0 ")); ASSERT_EQUALS(false, MathLib::isFloat(" 0")); ASSERT_EQUALS(true , MathLib::isFloat("0.")); ASSERT_EQUALS(false , MathLib::isFloat("0. ")); ASSERT_EQUALS(false , MathLib::isFloat(" 0. ")); ASSERT_EQUALS(false , MathLib::isFloat(" 0.")); ASSERT_EQUALS(false , MathLib::isFloat("0..")); ASSERT_EQUALS(false , MathLib::isFloat("..0..")); ASSERT_EQUALS(false , MathLib::isFloat("..0")); ASSERT_EQUALS(true , MathLib::isFloat("0.0")); ASSERT_EQUALS(true , MathLib::isFloat("-0.")); ASSERT_EQUALS(true , MathLib::isFloat("+0.")); ASSERT_EQUALS(true , MathLib::isFloat("-0.0")); ASSERT_EQUALS(true , MathLib::isFloat("+0.0")); ASSERT_EQUALS(true , MathLib::isFloat("0E0")); ASSERT_EQUALS(true , MathLib::isFloat("+0E0")); ASSERT_EQUALS(true , MathLib::isFloat("+0E0")); ASSERT_EQUALS(true , MathLib::isFloat("+0E+0")); ASSERT_EQUALS(true , MathLib::isFloat("+0E-0")); ASSERT_EQUALS(true , MathLib::isFloat("-0E+0")); ASSERT_EQUALS(true , MathLib::isFloat("-0E-0")); ASSERT_EQUALS(true , MathLib::isFloat("+0.0E+1")); ASSERT_EQUALS(true , MathLib::isFloat("+0.0E-1")); ASSERT_EQUALS(true , MathLib::isFloat("-0.0E+1")); ASSERT_EQUALS(true , MathLib::isFloat("-0.0E-1")); ASSERT_EQUALS(false , MathLib::isFloat("1")); ASSERT_EQUALS(false , MathLib::isFloat("-1")); ASSERT_EQUALS(false , MathLib::isFloat("+1")); ASSERT_EQUALS(true , MathLib::isFloat("+1e+1")); ASSERT_EQUALS(true , MathLib::isFloat("+1E+1")); ASSERT_EQUALS(true , MathLib::isFloat("+1E+100")); ASSERT_EQUALS(true , MathLib::isFloat("+1E+100f")); ASSERT_EQUALS(true , MathLib::isFloat("+1E+007")); // to be sure about #5485 ASSERT_EQUALS(true , MathLib::isFloat("+1E+001f")); ASSERT_EQUALS(false , MathLib::isFloat("+1E+001f2")); ASSERT_EQUALS(true , MathLib::isFloat("+1E+10000")); ASSERT_EQUALS(true , MathLib::isFloat("-1E+1")); ASSERT_EQUALS(true , MathLib::isFloat("-1E+10000")); ASSERT_EQUALS(true , MathLib::isFloat(".1250E+04")); ASSERT_EQUALS(true , MathLib::isFloat("-1E-1")); ASSERT_EQUALS(true , MathLib::isFloat("-1E-10000")); ASSERT_EQUALS(true , MathLib::isFloat("+1.23e+01")); ASSERT_EQUALS(true , MathLib::isFloat("+1.23E+01")); ASSERT_EQUALS(true , MathLib::isFloat("0.4")); ASSERT_EQUALS(true , MathLib::isFloat("2352.3f")); ASSERT_EQUALS(true , MathLib::isFloat("0.00004")); ASSERT_EQUALS(true , MathLib::isFloat("2352.00001f")); ASSERT_EQUALS(true , MathLib::isFloat(".4")); ASSERT_EQUALS(true , MathLib::isFloat(".3e2")); ASSERT_EQUALS(true , MathLib::isFloat("1.0E+1")); ASSERT_EQUALS(true , MathLib::isFloat("1.0E-1")); ASSERT_EQUALS(true , MathLib::isFloat("-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 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 unsigend 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() { // 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 } }; REGISTER_TEST(TestMathLib) cppcheck-1.66/test/testmemleak.cpp000066400000000000000000006471501236713773000172520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenlist.h" #include "checkmemoryleak.h" #include "testsuite.h" #include "symboldatabase.h" #include "preprocessor.h" #include extern std::ostringstream errout; class TestMemleak : private TestFixture { public: TestMemleak() : TestFixture("TestMemleak") { } private: void run() { TEST_CASE(testFunctionReturnType); TEST_CASE(open); } CheckMemoryLeak::AllocType functionReturnType(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; // 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(""); Settings settings; settings.standards.posix = true; 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)); } }; static TestMemleak testMemleak; class TestMemleakInFunction : public TestFixture { public: TestMemleakInFunction() : TestFixture("TestMemleakInFunction") { } private: Settings settings1; void check(const char code[], const Settings *settings = nullptr) { // Clear the error buffer.. errout.str(""); if (!settings) settings = &settings1; // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, settings, this); checkMemoryLeak.checkReallocUsage(); checkMemoryLeak.check(); } void run() { LOAD_LIB_2(settings1.library, "gtk.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(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(forwhile5); TEST_CASE(forwhile6); TEST_CASE(forwhile8); // Bug 2429936 TEST_CASE(forwhile9); TEST_CASE(forwhile10); TEST_CASE(forwhile11); TEST_CASE(switch2); TEST_CASE(switch3); TEST_CASE(switch4); // #2555 - segfault 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(throw1); TEST_CASE(throw2); TEST_CASE(linux_list_1); 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(assign2); // #2806 - FP when using redundant assignment 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); // 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); // Using the function "exit" TEST_CASE(exit2); TEST_CASE(exit4); TEST_CASE(exit5); TEST_CASE(exit6); TEST_CASE(exit7); TEST_CASE(noreturn); TEST_CASE(stdstring); 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(garbageCode); TEST_CASE(ptrptr); // test that the cfg files are configured correctly TEST_CASE(posixcfg); } std::string getcode(const char code[], const char varname[], bool classfunc=false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.standards.posix = true; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (!tokenizer.tokenize(istr, "test.cpp")) return ""; tokenizer.simplifyTokenList2(); const unsigned int varId(Token::findmatch(tokenizer.tokens(), varname)->varId()); // getcode.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, nullptr); checkMemoryLeak.parse_noreturn(); std::list callstack; callstack.push_back(0); CheckMemoryLeak::AllocType allocType, deallocType; allocType = deallocType = CheckMemoryLeak::No; Token *tokens = checkMemoryLeak.getcode(tokenizer.tokens(), 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")); ASSERT_EQUALS(";;alloc;", getcode("int *a = new int;", "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(";;alloc;", getcode("int i = open(a,b);", "i")); ASSERT_EQUALS(";;assign;", getcode("int i = open();", "i")); // 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")); ASSERT_EQUALS(";;if{}", getcode("char *s; if (a && s) { }", "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(";;alloc;use;", getcode("Fred *fred; p.setFred(fred = new Fred);", "fred")); 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(";;exit;", getcode("char *s; _exit(0);", "s")); ASSERT_EQUALS(";;exit;", getcode("char *s; abort();", "s")); ASSERT_EQUALS(";;exit;", getcode("char *s; err(0);", "s")); ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { exit(0); }", "s")); // list_for_each ASSERT_EQUALS(";;exit;{}", getcode("char *s; list_for_each(x,y,z) { }", "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")); // 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")); } void call_func() const { // whitelist.. ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("qsort")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("scanf")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("sscanf")); // #1293 ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("time")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("asctime")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("asctime_r")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("ctime")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("ctime_r")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("gmtime")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("gmtime_r")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("localtime")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("localtime_r")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("memcmp")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("gets")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vprintf")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vfprintf")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vsprintf")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("snprintf")); ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vsnprintf")); static const char * const call_func_white_list[] = { "access", "asprintf", "atof", "atoi", "atol", "chdir", "chmod", "clearerr", "chown", "delete" , "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 = CheckMemoryLeakInFunction::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(""); Settings settings; // Tokenize.. std::istringstream istr(code); TokenList list(&settings); list.createTokens(istr,"test.cpp"); Token *tokens=list.front(); // replace "if ( ! var )" => "if(!var)" for (Token *tok = tokens; 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(nullptr, &settings, this); checkMemoryLeak.simplifycode(tokens); return list.front()->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 static unsigned int dofindleak(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.debug = settings.debugwarnings = true; // Tokenize.. std::istringstream istr(code); TokenList list(&settings); 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); return (tok ? tok->linenr() : (unsigned int)(-1)); } void findleak() const { 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()); check("void Fred::aaa()\n" "{ }\n" "\n" "void Fred::foo()\n" "{\n" " gchar *s = NULL;\n" " if (a)\n" " s = g_malloc(10);\n" " else if (b)\n" " s = g_malloc(10);\n" " else\n" " f();\n" " g(s);\n" " if (c)\n" " h(s);\n" " g_free(s);\n" "}"); 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()); check("int f()\n" "{\n" " static gchar *s = 0;\n" " g_free(s);\n" " s = g_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() { Settings settings; settings.experimental = true; 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", &settings); 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()); check("void f()\n" "{\n" " gchar *s;\n" " bool b = true;\n" " if (b && (s = g_malloc(256)))\n" " ;\n" " if (b)\n" " g_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()); check("static void f(int i)\n" "{\n" " gchar *c = g_malloc(50);\n" " if (i == 1)\n" " {\n" " g_free(c);\n" " return;\n" " }\n" " if (i == 2)\n" " {\n" " return;\n" " }\n" " g_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() { Settings settings; settings.experimental = true; check("void foo()\n" "{\n" " int *x = new int[10];\n" " if (x == 0 || aa)\n" " {\n" " return 1;\n" " }\n" " delete [] x;\n" "}\n", &settings); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: x\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() { Settings settings; settings.experimental = true; 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", &settings); ASSERT_EQUALS("[test.cpp:9]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void forwhile10() { Settings settings; settings.experimental = true; 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" "}\n", &settings); 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 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 switch4() { // See tickets #2518 #2555 #4171 ASSERT_THROW(check("void f() {\n" " switch MAKEWORD(1)\n" " {\n" " case 0:\n" " return;\n" " }\n" "}"), InternalError); } 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() { Settings settings; settings.experimental = true; check("void f()\n" "{\n" " int *a = new int[10];\n" " free(a);\n" "}\n", &settings); 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", &settings); 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", &settings); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching allocation and deallocation: a\n", 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()); check("void f(gchar *buf)\n" "{\n" " int i;\n" " buf = g_malloc(3);\n" " buf[i] = 0;\n" " g_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() { Settings settings; settings.experimental = true; 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" "}\n", &settings); 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()); check("static void foo(gchar *a, gchar *b)\n" "{\n" " g_free(a);\n" " g_free(b);\n" "}\n" "static void f()\n" "{\n" " gchar *p = g_malloc(100);\n" " foo(p);\n" " g_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()); check("static void a()\n" "{ return true; }\n" "\n" "static void b()\n" "{\n" " gchar *p = g_malloc(100);\n" " if (a()) return;\n" // <- memory leak " g_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()); 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" " g_free(va_arg(a, void *));\n" " }\n" " va_end(a);\n" "}\n" "\n" "static gchar* foo()\n" "{\n" " return g_strdup(\"\");\n" "}\n" "\n" "static void bar()\n" "{\n" " int *p = g_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()); check("bool a(int *p) {\n" " return p;\n" "}\n" "\n" "void b() {\n" " int *p = g_malloc(16);\n" " if (!a(p)) return;\n" " g_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" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (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()); check("gchar *a()\n" "{\n" " return g_malloc(10);\n" "}\n" "static void b()\n" "{\n" " gchar *p = a();\n" " g_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()); check("void foo(gchar **str)\n" "{\n" " g_free(*str);\n" " *str = g_malloc(20);\n" "}\n" "\n" "void bar()\n" "{\n" " gchar *tmp = g_malloc(10);\n" " foo(&tmp);\n" " foo(&tmp);\n" " g_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()); } 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()); check("static FILE* data()\n" "{\n" " return fopen(\"data.txt\",\"rt\");\n" "}\n" "\n" "static void foo()\n" "{\n" " gchar* expr;\n" " func(&expr);\n" "\n" " FILE *f = data();\n" " fclose(f);\n" "\n" " g_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()); } 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 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 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()); 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 *) g_malloc(sizeof(*s2));\n" "\n" " if (s2->value != 0)\n" " return;\n" "\n" " g_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" " gchar *a = (gchar *)g_malloc(10);\n" " a = 0;\n" " g_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" " gchar *a = (gchar *)g_malloc(10);\n" " gchar *p = a;\n" " g_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" " gchar *a = (gchar *)g_malloc(10);\n" " gchar *p = a + 1;\n" " g_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" " gchar *a = (gchar *)g_malloc(10);\n" " a += 10;\n" " g_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" " gchar *a = (gchar *)g_malloc(10);\n" " a = (void *)a + 10;\n" " g_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 assign2() { // #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()); } 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()); check("void foo()\n" "{\n" " gchar *p = g_malloc(100);\n" " {\n" " gchar *p = 0;\n" " delete p;\n" " }\n" " g_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()); check("void foo()\n" "{\n" " char *a = reinterpret_cast(g_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 f()\n" "{\n" " gchar *str;\n" " g_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()); check("void foo()\n" "{\n" " gchar *str = 0;\n" " g_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" " gchar *str = g_malloc(10);\n" " g_free(str);\n" " gchar 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" " gchar *str = g_malloc(10);\n" " g_free(str);\n" " gchar 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" "{\n" " gchar *str = g_malloc(10);\n" " g_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() {\n" " gchar *str = g_malloc(10);\n" " g_free(str);\n" " g_strlcpy(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()); check("void foo(int x) {\n" " gchar *str = g_malloc(10);\n" " g_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()); check("void f(gchar *s) {\n" " g_free(s);\n" " g_strlcpy(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 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()); check("void foo()\n" "{\n" " gchar *str = g_malloc(100);\n" " g_free(str);\n" " g_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()); check("void foo()\n" "{\n" " gchar *p = g_malloc(10);\n" " g_free(p);\n" " bar(&p);\n" " g_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()); check("void foo()\n" "{\n" " char *p1 = g_malloc(10);\n" " char *p2 = g_strlcpy(p1, \"a\");\n" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:5]: (error) Memory leak: p1\n", 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()); check("void foo()\n" "{\n" " gchar *p = g_malloc(10);\n" " p[0] = 0;\n" " p = g_strcat( p, \"a\" );\n" " g_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 given size 3 is mismatching\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 given size 3 is mismatching\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()); check("static void foo()\n" "{\n" " gchar *p = NULL;\n" "\n" " if( a )\n" " p = g_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() { Settings settings; settings.experimental = true; check("void f()\n" "{\n" " char *a = new char[10];\n" " if (!a && b() )\n" " return;\n" "\n" " delete [] a;\n" "}\n", &settings); 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" "}\n", &settings); ASSERT_EQUALS("", errout.str()); } void assign_pclose() { Settings settings; settings.standards.posix = true; check("void f()\n" "{\n" " FILE *f = popen (\"test\", \"w\");\n" " int a = pclose(f);\n" "}", &settings); ASSERT_EQUALS("", 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 stdstring() { check("void f(std::string foo)\n" "{\n" " char *out = new char[11];\n" " memset(&(out[0]), 0, 1);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: out\n", errout.str()); } void strndup_function() { check("void f()\n" "{\n" " char *out = strndup(\"text\", 3);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (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() { Settings settings; settings.experimental = true; settings.standards.posix = true; check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" "}\n", &settings); 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" "}\n", &settings); 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" "}\n", &settings); 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" "}\n", &settings); ASSERT_EQUALS("", errout.str()); } void creat_function() { Settings settings; settings.standards.posix = true; check("void f(const char *path)\n" "{\n" " int fd = creat(path, S_IRWXU);\n" "}", &settings); ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fd\n", errout.str()); } void close_function() { Settings settings; settings.standards.posix = true; check("void f(const char *path)\n" "{\n" " int fd = open(path, O_RDONLY);\n" " close(fd);\n" "}", &settings); ASSERT_EQUALS("", errout.str()); check("void f(const char *path)\n" "{\n" " int fd = creat(path, S_IRWXU);\n" " close(fd);\n" "}", &settings); 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" "}", &settings); 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" "}", &settings); 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" "}", &settings); ASSERT_EQUALS("[test.cpp:11]: (error) Resource leak: handle\n", errout.str()); } void fd_functions() { Settings settings; settings.standards.posix = true; 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" "}", &settings); 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()); check("void foo()\n" "{\n" " void *sym = ( {\n" " void *__ptr = g_malloc(100);\n" " if(!__ptr && 100 != 0)\n" " {\n" " g_exit(1);\n" " }\n" " __ptr;\n" " } );\n" " g_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 garbageCode() { ASSERT_THROW(check("void h(int l) {\n" " while\n" // Don't crash (#3870) "}"), InternalError); } void ptrptr() { check("void f() {\n" " char *p;\n" " char **pp = &p;\n" " *pp = calloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); } // Test that posix.cfg is configured correctly void posixcfg() { Settings settings; settings.standards.posix = true; LOAD_LIB_2(settings.library, "posix.cfg"); const char code[] = "void leaks() {\n" " void* leak1 = fdopendir();\n" " void* leak2 = opendir();\n" " void* leak3 = socket();\n" "}\n" "void noleaks() {\n" " void *p1 = fdopendir(); closedir(p1);\n" " void *p2 = opendir(); closedir(p2);\n" " void *p3 = socket(); close(p3);\n" "}"; check(code, &settings); ASSERT_EQUALS("[test.cpp:5]: (error) Resource leak: leak1\n" "[test.cpp:5]: (error) Resource leak: leak2\n" "[test.cpp:5]: (error) Resource leak: leak3\n", errout.str()); const char code2[] = "int main() {\n" " int fileDescriptor = socket(AF_INET, SOCK_STREAM, 0);\n" " close(fileDescriptor);\n" "}"; check(code2, &settings); ASSERT_EQUALS("", errout.str()); // Ticket #2830 check("void f(const char *path) {\n" " int fd = open(path, O_RDONLY);\n" " FILE *f = fdopen(fd, x);\n" " fclose(f);\n" "}", &settings); ASSERT_EQUALS("", errout.str()); // Ticket #1416 check("void f(void) {\n" " FILE *f = fdopen(0, \"r\");\n" "}", &settings); ASSERT_EQUALS("[test.cpp:3]: (error) Resource leak: f\n", errout.str()); LOAD_LIB_2(settings.library, "gtk.cfg"); check("void f(char *a) {\n" " char *s = g_strdup(a);\n" " mkstemp(s);\n" " mkdtemp(s);\n" " mktemp(s);\n" "}", &settings); ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: s\n", errout.str()); } }; static TestMemleakInFunction testMemleakInFunction; class TestMemleakInClass : public TestFixture { public: TestMemleakInClass() : TestFixture("TestMemleakInClass") { } private: /** * Tokenize and execute leak check for given code * @param code Source code */ void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); settings.addEnabled("style"); // 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() { 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" " gchar *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" " g_free(str1);\n" "}"); TODO_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()); 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" "};"); TODO_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" " ~A() { g_free(p); }\n" "};\n" "A::A()\n" "{ p = g_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()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = g_malloc(sizeof(int)*10); }\n" " ~A() { g_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:9]: (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:6]: (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:9]: (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:16]: (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:16]: (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" "private:\n" " gchar *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { g_free(s); }\n" " void xy()\n" " { s = g_malloc(100); }\n" "};"); TODO_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()); check("class Fred\n" "{\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { g_free(s); }\n" " void xy()\n" " { s = g_malloc(100); }\n" "private:\n" " char *s;\n" "};"); TODO_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()); check("class Fred\n" "{\n" "private:\n" " gchar *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { g_free(s); }\n" " const Fred & operator = (const Fred &f)\n" " { s = g_malloc(100); }\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", "", errout.str()); } }; static TestMemleakInClass testMemleakInClass; class TestMemleakStructMember : public TestFixture { public: TestMemleakStructMember() : TestFixture("TestMemleakStructMember") { } private: void check(const char code[], const char fname[] = 0, bool isCPP = true) { // Clear the error buffer.. errout.str(""); Settings settings; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, fname ? fname : (isCPP ? "test.cpp" : "test.c")); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakStructMember checkMemoryLeakStructMember(&tokenizer, &settings, this); checkMemoryLeakStructMember.check(); } void run() { // 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 } 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 = g_malloc(sizeof(struct ABC));\n" " abc->a = g_malloc(10);\n" " g_free(abc);\n" "}"); TODO_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 void foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abc->a = g_malloc(10);\n" "}"); TODO_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 ABC * foo()\n" "{\n" " ABC *abc = g_malloc(sizeof(ABC));\n" " abc->a = g_malloc(10);\n" " abc->b = g_malloc(10);\n" " if (abc->b == 0)\n" " {\n" " return 0;\n" " }\n" " return abc;\n" "}"); TODO_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()); check("static void foo(int a)\n" "{\n" " ABC *abc = g_malloc(sizeof(ABC));\n" " abc->a = g_malloc(10);\n" " if (a == 1)\n" " {\n" " g_free(abc->a);\n" " return;\n" " }\n" "}"); TODO_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()); check("static void foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abc->a = g_malloc(10);\n" " if (abc->a)\n" " { goto out; }\n" " g_free(abc);\n" " return;\n" "out:\n" " g_free(abc->a);\n" " g_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 ABC * foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abc->a = g_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()); check("static void foo(struct ABC *abc)\n" "{\n" " abc->a = g_malloc(10);\n" "}"); ASSERT_EQUALS("", 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()); check("static ABC * foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abc->a = g_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 = abc1;\n" " abc->a = g_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 ABC *abc;\n" " abc1 = abc = g_malloc(sizeof(ABC));\n" " abc->a = g_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()); check("static void foo()\n" "{\n" " struct msn_entry *ptr;\n" " ptr = g_malloc(sizeof(struct msn_entry));\n" " ptr->msn = g_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()); check("static void foo() {\n" " struct ABC *abc = g_malloc(123);\n" " abc->a = abc->b = g_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" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(struct s *f1) {\n" " struct s f2;\n" " f2.a = g_malloc(100);\n" " *f1 = f2;\n" "}\n", "test.c"); 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()); check("static struct ABC * foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abc->a = g_malloc(10);\n" " if (!abc->a)\n" " {\n" " g_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 = g_malloc(sizeof(struct ABC));\n" " abc->a = g_malloc(10);\n" " g_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()); check("static void foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abclist.push_back(abc);\n" " abc->a = g_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" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " A a = { 0 };\n" " a.foo = (gchar *) g_malloc(10);\n" " assign(&a);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); } // #3024: kernel list void function3() { check("void f() {\n" " struct ABC *abc = kmalloc(100);\n" " abc.a = (char *) kmalloc(10);\n" " list_add_tail(&abc->list, head);\n" "}\n", "test.c"); 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" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void a(char *p) { gchar *x = p; g_free(x); }\n" "void b() {\n" " struct ABC abc;\n" " abc.a = (gchar *) g_malloc(10);\n" " a(abc.a);\n" "}\n", "test.c"); 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()); check("static void foo()\n" "{\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " if (x)" " {\n" " abc->a = g_malloc(10);\n" " }\n" " else\n" " {\n" " g_free(abc);\n" " return;\n" " }\n" " g_free(abc->a);\n" " g_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()); check("static void foo() {\n" " struct ABC *abc = g_malloc(sizeof(struct ABC));\n" " abc->next = g_malloc(sizeof(struct ABC));\n" " abc->next->next = NULL;\n" "\n" " while (abc) {\n" " struct ABC *next = abc->next;\n" " g_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()); check("struct ABC *abc;\n" "\n" "static void foo()\n" "{\n" " abc = g_malloc(sizeof(struct ABC));\n" " abc->a = g_malloc(10);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #933 Leaks with struct members not detected void localvars() { // Test error case const char code_err[] = "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" "}\n"; check(code_err, "test.cpp"); ASSERT_EQUALS("", errout.str()); check(code_err, "test.c"); ASSERT_EQUALS("[test.c:12]: (error) Memory leak: a.f\n" "[test.c:12]: (error) Memory leak: a.c\n" "[test.c:12]: (error) Memory leak: a.m\n", errout.str()); const char code_err_glib[] = "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 = g_malloc(12);\n" "}\n"; check(code_err_glib, "test.cpp"); ASSERT_EQUALS("", errout.str()); check(code_err_glib, "test.c"); TODO_ASSERT_EQUALS("[test.c:12]: (error) Memory leak: a.f\n" "[test.c:12]: (error) Memory leak: a.c\n" "[test.c:12]: (error) Memory leak: a.m\n", "[test.c:12]: (error) Memory leak: a.f\n" "[test.c:12]: (error) Memory leak: a.c\n", errout.str()); // Test OK case const char code_ok[] = "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" "}\n"; check(code_ok, "test.cpp"); ASSERT_EQUALS("", errout.str()); check(code_ok, "test.c"); ASSERT_EQUALS("", errout.str()); const char code_ok_glib[] = "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 = g_malloc(12);\n" " fclose(a.f);\n" " delete [] a.c;\n" " g_free(a.m);\n" "}\n"; check(code_ok_glib, "test.cpp"); ASSERT_EQUALS("", errout.str()); check(code_ok_glib, "test.c"); 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" "}", /*fname=*/0, /*isCPP=*/false); ASSERT_EQUALS("[test.c:9]: (error) Memory leak: s.state_check_buff\n", errout.str()); check("struct S {\n" " void *state_check_buff;\n" "};\n" "void f() {\n" " S s;\n" " (s).state_check_buff = (void* )g_malloc(1);\n" " if (s.state_check_buff == 0)\n" " return;\n" "}", /*fname=*/0, /*isCPP=*/false); TODO_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" "}", /*fname=*/0, /*isCPP=*/false); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: f.realm\n", errout.str()); } }; static TestMemleakStructMember 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.standards.posix = true; LOAD_LIB_2(settings.library, "gtk.cfg"); // Add some test allocation functions to the library. // When not run as a unit test, these are read from // an XML file (e.g. cfg/posix.cfg). int id = 0; while (!settings.library.ismemory(++id)) continue; settings.library.setalloc("malloc", id); settings.library.setalloc("calloc", id); settings.library.setalloc("strdup", id); // pass allocated memory to function.. TEST_CASE(functionParameter); // never use leakable resource TEST_CASE(missingAssignment); } 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("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("char *x() {\n" " char *ret = strcpy(malloc(10), \"abc\");\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("gchar *x() {\n" " gchar *ret = g_strlcpy(g_malloc(10), \"abc\");\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x() {\n" " free(malloc(10));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x() {\n" " g_free(g_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" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with strdup, set_error doesn't release it.\n", errout.str()); check("void set_error(const char *msg) {\n" "}\n" "\n" "void x() {\n" " set_error(g_strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with g_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(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 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" " if(!g_strcmp0(g_strdup(a), b) == 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with g_strdup, g_strcmp0 doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " 42, strcmp(strdup(a), b);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " 42, 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()); } void missingAssignment() { check("void x()\n" "{\n" " malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function malloc is not used.\n", errout.str()); check("void x()\n" "{\n" " calloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function calloc is not used.\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 used.\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 used.\n", errout.str()); check("void x()\n" "{\n" " char* ptr = malloc(10);\n" " foo(ptr);\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char** x(const char* str) {\n" " char* ptr[] = { malloc(10), malloc(5), strdup(str) };\n" " return ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x()\n" "{\n" " 42,malloc(42);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function malloc is not used.\n", "", errout.str()); check("void *f()\n" "{\n" " return malloc(10);\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function f is not used.\n", "", 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(""); // Preprocess... Preprocessor preprocessor(&settings, this); std::istringstream istrpreproc(code); std::map actual; preprocessor.preprocess(istrpreproc, actual, "test.c"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(actual[""]); 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, "gtk.cfg"); TEST_CASE(glib1); } 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.c:1]: (error) Memory leak: a\n", errout.str()); } }; static TestMemleakGLib testMemleakGLib; class TestMemleakWindows : public TestFixture { public: TestMemleakWindows() : TestFixture("TestMemleakWindows") { } private: Settings settings; void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Preprocess... Preprocessor preprocessor(&settings, this); std::istringstream istrpreproc(code); std::map actual; preprocessor.preprocess(istrpreproc, actual, "test.c"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(actual[""]); 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); } 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()); } }; static TestMemleakWindows testMemleakWindows; cppcheck-1.66/test/testnonreentrantfunctions.cpp000066400000000000000000000114641236713773000222760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checknonreentrantfunctions.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestNonReentrantFunctions : public TestFixture { public: TestNonReentrantFunctions() : TestFixture("TestNonReentrantFunctions") { } private: void run() { TEST_CASE(test_crypt); TEST_CASE(test_namespace_handling); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.standards.posix = true; settings.addEnabled("portability"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check for non reentrant functions.. CheckNonReentrantFunctions checkNonReentrantFunctions(&tokenizer, &settings, this); checkNonReentrantFunctions.nonReentrantFunctions(); } void test_crypt() { check("void f(char *pwd)\n" "{\n" " char *cpwd;" " crypt(pwd, cpwd);\n" "}"); ASSERT_EQUALS("[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]: (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 test_namespace_handling() { 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\n\", 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]: (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()); } }; REGISTER_TEST(TestNonReentrantFunctions) cppcheck-1.66/test/testnullpointer.cpp000066400000000000000000002725241236713773000202110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checknullpointer.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestNullPointer : public TestFixture { public: TestNullPointer() : TestFixture("TestNullPointer") { } private: Settings settings; void run() { LOAD_LIB_2(settings.library, "std.cfg"); 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(nullpointer14); 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(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(crash1); TEST_CASE(functioncallDefaultArguments); TEST_CASE(nullpointer_internal_error); // #5080 TEST_CASE(nullpointerFputc); // #5645 FP: Null pointer dereference in fputc argument TEST_CASE(nullpointerMemchr); TEST_CASE(nullpointerPutchar); // Test that std.cfg is configured correctly TEST_CASE(stdcfg); // Load posix library file LOAD_LIB_2(settings.library, "posix.cfg"); // Test that posix.cfg is configured correctly TEST_CASE(posixcfg); } void check(const char code[], bool inconclusive = false, const char filename[] = "test.cpp", bool verify=true) { // Clear the error buffer.. errout.str(""); settings.addEnabled("warning"); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (!tokenizer.tokenize(istr, filename)) return; // Check for redundant code.. CheckNullPointer checkNullPointer(&tokenizer, &settings, this); checkNullPointer.nullPointer(); const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); if (verify && str1 != str2) warn(("Unsimplified code in test case. It looks like this test " "should either be cleaned up or moved to TestTokenizer or " "TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str()); checkNullPointer.nullConstantDereference(); } void nullpointerAfterLoop() { check("int foo(const Token *tok)\n" "{\n" " while (tok);\n" " tok = tok->next();\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\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:6] -> [test.cpp:3]: (warning, inconclusive) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\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:3] -> [test.cpp:5]: (warning) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\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" "}", false, "test.cpp", false); 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, "test.cpp", false); 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:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:5]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n" "[test.cpp:3] -> [test.cpp:5]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n" "[test.cpp:4] -> [test.cpp:5]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(ABC *abc) {\n" " if (abc->a == 3) {\n" " return;\n" " }\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n", errout.str()); check("void f(ABC *abc) {\n" " if (abc->x == 0) {\n" " return;\n" " }\n" " if (!abc);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:3]: (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:2] -> [test.cpp:3]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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, "test.cpp", false); 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" "}",false,"test.cpp",false); 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" "}",false,"test.cpp",false); 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;\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:4]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:3]: (warning, inconclusive) Possible null pointer dereference: fred - otherwise it is redundant to check it against null.\n", errout.str()); } // #3425 - false positives when there are macros check("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:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (p || q) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(int *p)\n" "{\n" " bar(*p);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p)\n" "{\n" " strcpy(p, \"abc\");\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p)\n" "{\n" " if (*p == 0) { }\n" " if (!p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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:3] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:3]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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,"test.cpp",false); 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,"test.cpp",false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = (a) ? b : b+1;\n" "}\n",true,"test.cpp",false); 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:2] -> [test.cpp:4]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:4]: (warning) Possible null pointer dereference: item - otherwise it is redundant to check it against null.\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, "test.cpp", false); ASSERT_EQUALS("", errout.str()); check("void f(type* p) {\n" // #4983 " x(sizeof p[0]);\n" " if (!p)\n" " ;\n" "}", false, "test.cpp", false); 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 check("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) Possible 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) Possible 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]: (error) 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) Possible 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) Possible 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) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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" "}"); TODO_ASSERT_EQUALS("[test.cpp:11]: (error) 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" "}", false, "test.cpp", false); ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: x\n" "[test.cpp:4]: (error) Null pointer dereference\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) Possible 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) Possible 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", false); // C++ file => nullptr means NULL ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: i\n" "[test.cpp:4]: (error) Null pointer dereference\n", errout.str()); check(code, false, "test.c", false); // C file => nullptr does not mean NULL ASSERT_EQUALS("", errout.str()); } void nullpointer14() { check("void foo()\n" "{\n" " strcpy(bar, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference\n", errout.str()); check("void foo()\n" "{\n" " memcmp(bar(xyz()), 0, 123);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference\n", errout.str()); check("void foo(const char *s)\n" "{\n" " char *p = malloc(100);\n" " frexp(1.0, p);\n" " char *q = 0;\n" " frexp(1.0, q);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Possible null pointer dereference: q\n", 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" "}\n", false, "test.cpp", false); ASSERT_EQUALS("[test.cpp:5]: (error) Possible null pointer dereference: str\n" "[test.cpp:5]: (error) Null pointer dereference\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]: (error) 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) Possible 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 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, "test.cpp", false); ASSERT_EQUALS("[test.cpp:7]: (error) 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:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (NULL == p) {\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (p == NULL) {\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (p == NULL) {\n" " }\n" " printf(\"%c\", *p);\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (p && *p == 0) {\n" " }\n" " printf(\"%c\", *p);\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (p && *p == 0) {\n" " } else { *p = 0; }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (p) {\n" " }\n" " strcpy(p, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("void foo(char *p) {\n" " if (p) {\n" " }\n" " bar();\n" " strcpy(p, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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" "}", false, "test.cpp", false); 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:5] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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()); // #2582 - segmentation fault check("if()"); // #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, "test.cpp", false); 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:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: fred - otherwise it is redundant to check it against null.\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:4] -> [test.cpp:3]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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); // non-inconclusive ASSERT_EQUALS("", errout.str()); check(code, true); // inconclusive ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning, inconclusive) Possible null pointer dereference: fred - otherwise it is redundant to check it against null.\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:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); check("int f(ABC *p) {\n" // FP : return && " if (!p) {}\n" " return p && p->x;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Test CheckNullPointer::nullConstantDereference void nullConstantDereference() { // Ticket #2090 check("void foo() {\n" " strcpy(0, \"abcd\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); // Ticket #1171 check("void foo(void* bar) {\n" " if(strcmp(0, bar) == 0)\n" " func();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); // Ticket #2413 - it's ok to pass NULL to fflush check("void foo() {\n" " fflush(NULL);\n" "}", true); ASSERT_EQUALS("", errout.str()); // Ticket #3126 - don't confuse member function with standard function check("void f() {\n" " image1.fseek(0, SEEK_SET);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int* p = 0;\n" " return p[4];\n" "}", false, "test.cpp", false); ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: p\n" "[test.cpp:3]: (error) Null pointer dereference\n", errout.str()); check("void f(int x) {\n" // #4809 - passing "NULL" " itoa(x,NULL,10);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " typeof(*NULL) y;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() { freopen(NULL, m, stdin); }"); 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) Possible null pointer dereference: s\n", errout.str()); check("void f() {\n" " char *s = 0;\n" " printf(\"%s\", s == 0 ? a : s);\n" "}", false, "test.cpp", false); 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) Possible 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) Possible 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]: (error) 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, "test.cpp", false); ASSERT_EQUALS("", errout.str()); check("struct NonPolymorphicA { ~A() {} };\n" "bool foo() {\n" " NonPolymorphicA* a = 0;\n" " return typeid(*a) == typeid(*a);\n" "}", true, "test.cpp", false); ASSERT_EQUALS("", errout.str()); check("bool foo() {\n" " char* c = 0;\n" " return typeid(*c) == typeid(*c);\n" "}", true, "test.cpp", false); 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" " 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:7]: (error) Possible null pointer dereference: p\n" "[test.cpp:8]: (error) Possible null pointer dereference: p\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" "[test.cpp:6]: (error) Null pointer dereference\n" /* TODO: handle std::string "[test.cpp:9]: (error) Possible null pointer dereference: p\n" "[test.cpp:10]: (error) Possible 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(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) Possible null pointer dereference: p\n" "[test.cpp:5]: (error) Possible null pointer dereference: p\n" "[test.cpp:7]: (error) Possible null pointer dereference: p\n" "[test.cpp:8]: (error) Possible null pointer dereference: p\n", 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()); } 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, "test.cpp", false); ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: p\n" "[test.cpp:4]: (error) Possible null pointer dereference: p\n" "[test.cpp:6] -> [test.cpp:5]: (warning) Possible null pointer dereference: q - otherwise it is redundant to check it against null.\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" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:5] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:6] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", "[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:4] -> [test.cpp:2]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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, "test.cpp", false); 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 postive: (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, "test.cpp", false); 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, "test.cpp", false); 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:4] -> [test.cpp:6]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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:4] -> [test.cpp:6]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n", errout.str()); // inconclusive check("void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning, inconclusive) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\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:4] -> [test.cpp:6]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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:4] -> [test.cpp:6]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\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:2] -> [test.cpp:4]: (warning, inconclusive) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n", errout.str()); } } void functioncalllibrary() { Settings settings1; Tokenizer tokenizer(&settings1,this); std::istringstream code("void f() { int a,b; x(a,b); }"); tokenizer.tokenize(code,"test.c"); const Token *xtok = Token::findsimplematch(tokenizer.tokens(), "x"); // nothing bad.. { Library library; Library::ArgumentChecks arg; library.argumentChecks["x"][1] = arg; library.argumentChecks["x"][2] = arg; std::list null, uninit; CheckNullPointer::parseFunctionCall(*xtok, null, &library, 0U); CheckNullPointer::parseFunctionCall(*xtok, uninit, &library, 1U); ASSERT_EQUALS(0U, null.size()); ASSERT_EQUALS(0U, uninit.size()); } // for 1st parameter null pointer is not ok.. { Library library; Library::ArgumentChecks arg; library.argumentChecks["x"][1] = arg; library.argumentChecks["x"][2] = arg; library.argumentChecks["x"][1].notnull = true; std::list null,uninit; CheckNullPointer::parseFunctionCall(*xtok, null, &library, 0U); CheckNullPointer::parseFunctionCall(*xtok, uninit, &library, 1U); ASSERT_EQUALS(1U, null.size()); ASSERT_EQUALS("a", null.front()->str()); ASSERT_EQUALS(0U, uninit.size()); } // for 2nd parameter uninit data is not ok.. { Library library; Library::ArgumentChecks arg; library.argumentChecks["x"][1] = arg; library.argumentChecks["x"][2] = arg; library.argumentChecks["x"][2].notuninit = true; std::list null,uninit; CheckNullPointer::parseFunctionCall(*xtok, null, &library, 0U); CheckNullPointer::parseFunctionCall(*xtok, uninit, &library, 1U); ASSERT_EQUALS(0U, null.size()); ASSERT_EQUALS(1U, uninit.size()); ASSERT_EQUALS("b", uninit.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" " 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" "}"); 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 "}",false,"test.cpp",false); 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; "}"); TODO_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" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); // The init() function may or may not initialize p, but since the address // of p is passed in, it's a good bet that p may be modified and // so we should not report an error. check("void f(int *p = 0) {\n" " init(&p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void init(int* &g);\n" "void f(int *p = 0) {\n" " init(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p == 0) {\n" " init(&p);\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p == 0) {\n" " throw SomeException;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p = 0) {\n" " int var1 = x ? *p : 5;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", "", errout.str()); } void crash1() { ASSERT_THROW(check("int f() {\n" " return if\n" "}"), InternalError); } 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()); } // Test that std.cfg is configured correctly void stdcfg() { const char errp[] = "[test.cpp:1] -> [test.cpp:1]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n"; const char errpq[] = "[test.cpp:1] -> [test.cpp:1]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n" "[test.cpp:1] -> [test.cpp:1]: (warning) Possible null pointer dereference: q - otherwise it is redundant to check it against null.\n"; check("void f(FILE *p){ clearerr (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(FILE *p){ feof (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(FILE *p){ fgetc (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(FILE *p){ fclose (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(FILE *p){ ferror (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(FILE *p){ ftell (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char *p){ puts (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char * p,char * q){ fopen (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,FILE * q){ fputc (*p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,FILE * q){ fputs (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(FILE * p,fpos_t * q){ fgetpos (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(FILE * p,fpos_t * q){ fsetpos (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p){ strchr (p,c);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char * p){ putchar (*p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char * p){ strlen (p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char * p,char * q){ strcpy (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strspn (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strcspn (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strcoll (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strcat (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strcmp (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strncpy (p,q,1);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strncat (p,q,1);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strncmp (p,q,1);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); check("void f(char * p,char * q){ strstr (p,q);if(!p||!q){}}"); ASSERT_EQUALS(errpq,errout.str()); // strtol etc check("void f(char * p,char * q){ strtoul (p,q,0);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char * p,char * q){ strtoull (p,q,0);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(char * p,char * q){ strtol (p,q,0);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); } void nullpointerFputc() { check("int main () {\n" "FILE *fp = fopen(\"file.txt\", \"w+\");\n" "fputc(000, fp); \n" "fclose(fp);\n" "return 0 ;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int main () {\n" "FILE *fp = fopen(\"file.txt\", \"w+\");\n" "char *nullstring=0;" "fputc(*nullstring, fp); \n" "fclose(fp);\n" "return 0 ;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: nullstring\n", errout.str()); } void nullpointerMemchr() { check("void f (char *p, char *s) {\n" " p = memchr (s, 'p', strlen(s));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f (char *p, char *s) {\n" " p = memchr (s, 0, strlen(s));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f (char *p) {\n" " char *s = 0;\n" " p = memchr (s, 0, strlen(s));\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: s\n", errout.str()); } void nullpointerPutchar() { check("void f (char *c) {\n" " putchar(c);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f () {\n" " putchar(0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f () {\n" " putchar(*0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); } void posixcfg() { const char errp[] = "[test.cpp:1] -> [test.cpp:1]: (warning) Possible null pointer dereference: p - otherwise it is redundant to check it against null.\n"; check("void f(FILE *p){ isatty (*p);if(!p){}}"); ASSERT_EQUALS(errp,errout.str()); check("void f(){ isatty (0);}"); ASSERT_EQUALS("",errout.str()); check("void f(char *p){ mkdir (p, 0);}"); ASSERT_EQUALS("",errout.str()); check("void f(char *p){ int i = 0; mkdir (p, i);}"); ASSERT_EQUALS("",errout.str()); check("void f(){ getcwd (0, 0);}"); ASSERT_EQUALS("",errout.str()); check("void f(char *p){ mkdir (p, *0);}"); ASSERT_EQUALS("[test.cpp:1]: (error) Null pointer dereference\n",errout.str()); check("void foo()\n" "{\n" " char const * x = 0;\n" " strdup(x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: x\n", errout.str()); } }; REGISTER_TEST(TestNullPointer) cppcheck-1.66/test/testobsoletefunctions.cpp000066400000000000000000000236461236713773000214020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkobsoletefunctions.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestObsoleteFunctions : public TestFixture { public: TestObsoleteFunctions() : TestFixture("TestObsoleteFunctions") { } private: void run() { TEST_CASE(testbsd_signal); TEST_CASE(testgethostbyname); TEST_CASE(testgethostbyaddr); TEST_CASE(testusleep); TEST_CASE(testindex); TEST_CASE(test_qt_index); // FP when using the Qt function 'index'? TEST_CASE(testrindex); // no false positives for variables TEST_CASE(testvar); // dangerous function TEST_CASE(testgets); TEST_CASE(testalloca); // declared function ticket #3121 TEST_CASE(test_declared_function); // test std::gets TEST_CASE(test_std_gets); // multiple use of obsolete functions TEST_CASE(test_multiple); // c declared function TEST_CASE(test_c_declaration); // function with body TEST_CASE(test_function_with_body); // null pointer dereference in obsoleteFunctions TEST_CASE(ticket3238); } void check(const char code[], const char filename[]="test.cpp") { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); settings.standards.posix = true; settings.standards.c = Standards::C11; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); tokenizer.simplifyTokenList2(); // Check for obsolete functions.. CheckObsoleteFunctions checkObsoleteFunctions(&tokenizer, &settings, this); checkObsoleteFunctions.obsoleteFunctions(); } void testbsd_signal() { check("void f()\n" "{\n" " bsd_signal(SIGABRT, SIG_IGN);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolete function 'bsd_signal' called. It is recommended to use the function 'sigaction' instead.\n", errout.str()); check("int f()\n" "{\n" " int bsd_signal(0);\n" " return bsd_signal;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testgethostbyname() { 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) Obsolete function 'gethostbyname' called. It is recommended to use the function 'getaddrinfo' instead.\n", errout.str()); } void testgethostbyaddr() { 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) Obsolete function 'gethostbyaddr' called. It is recommended to use the function 'getnameinfo' instead.\n", errout.str()); } void testusleep() { check("void f()\n" "{\n" " usleep( 1000 );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolete function 'usleep' called. It is recommended to use the 'nanosleep' or 'setitimer' function instead.\n", errout.str()); } void testindex() { 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) Obsolete function 'index' called. It is recommended to use the function 'strchr' instead.\n", errout.str()); } void test_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) Obsolete function 'index' called. It is recommended to use the function 'strchr' instead.\n", errout.str()); } void testrindex() { 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) Obsolete function 'rindex' called. It is recommended to use the function 'strrchr' instead.\n", errout.str()); } void testvar() { check("class Fred {\n" "public:\n" " Fred() : index(0) { }\n" " int index;\n" "};"); ASSERT_EQUALS("", errout.str()); } void testgets() { check("void f()\n" "{\n" " char *x = gets();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolete function 'gets' called. It is recommended to use the function 'fgets' instead.\n", errout.str()); check("void f()\n" "{\n" " foo(x, gets());\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolete function 'gets' called. It is recommended to use the function 'fgets' instead.\n", errout.str()); } void testalloca() { check("void f()\n" "{\n" " char *x = alloca(10);\n" "}\n", "test.cpp"); // #4382 - there are no VLAs in C++ ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (style) Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n", errout.str()); } // ticket #3121 void test_declared_function() { 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 test_std_gets() { check("void f(char * str)\n" "{\n" " char *x = std::gets(str);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolete function 'gets' called. It is recommended to use the function 'fgets' instead.\n", errout.str()); } // multiple use void test_multiple() { check("void f(char * str)\n" "{\n" " char *x = std::gets(str);\n" " usleep( 1000 );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolete function 'gets' called. It is recommended to use the function 'fgets' instead.\n" "[test.cpp:4]: (style) Obsolete function 'usleep' called. It is recommended to use the 'nanosleep' or 'setitimer' function instead.\n", errout.str()); } void test_c_declaration() { check("char * gets ( char * c ) ;\n" "int main ()\n" "{\n" " char s [ 10 ] ;\n" " gets ( s ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Obsolete function 'gets' called. It is recommended to use the function 'fgets' 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]: (style) Obsolete function 'getcontext' called. Due to portability issues, applications are recommended to be rewritten to use POSIX threads.\n", errout.str()); } void test_function_with_body() { check("char * gets ( char * c ) { return c; }\n" "int main ()\n" "{\n" " char s [ 10 ] ;\n" " gets ( s ) ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ticket3238() { check("__FBSDID(\"...\");\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestObsoleteFunctions) cppcheck-1.66/test/testoptions.cpp000066400000000000000000000062521236713773000173220ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public 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" #include extern std::ostringstream errout; 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(gcc_errors); 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 gcc_errors() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-g"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.gcc_style_errors()); } 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", "-g"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS("TestClass::TestMethod", args.which_test()); ASSERT_EQUALS(true, args.gcc_style_errors()); ASSERT_EQUALS(true, args.quiet()); } }; REGISTER_TEST(TestOptions) cppcheck-1.66/test/testother.cpp000066400000000000000000010066651236713773000167610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "symboldatabase.h" #include "checkother.h" #include "testsuite.h" #include #include extern std::ostringstream errout; class TestOther : public TestFixture { public: TestOther() : TestFixture("TestOther") { } private: void run() { TEST_CASE(oppositeInnerCondition); 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(zeroDivCond); // division by zero / useless condition TEST_CASE(nanInArithmeticExpression); TEST_CASE(invalidFunctionUsage1); TEST_CASE(sprintf1); // Dangerous usage of sprintf TEST_CASE(sprintf2); TEST_CASE(sprintf3); TEST_CASE(sprintf4); // struct member TEST_CASE(strPlusChar1); // "/usr" + '/' TEST_CASE(strPlusChar2); // "/usr" + ch TEST_CASE(strPlusChar3); // ok: path + "/sub" + '/' TEST_CASE(strPlusChar4); // ast 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(varScope19); // Ticket #4994 TEST_CASE(varScope20); // Ticket #5103 TEST_CASE(varScope21); // Ticket #5382 TEST_CASE(varScope22); // Ticket #5684 TEST_CASE(oldStylePointerCast); TEST_CASE(invalidPointerCast); TEST_CASE(passedByValue); 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(switchRedundantAssignmentTest); TEST_CASE(switchRedundantOperationTest); TEST_CASE(switchRedundantBitwiseOperationTest); TEST_CASE(switchFallThroughCase); 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(modulo); TEST_CASE(incorrectLogicOperator1); TEST_CASE(incorrectLogicOperator2); TEST_CASE(incorrectLogicOperator3); TEST_CASE(incorrectLogicOperator4); TEST_CASE(incorrectLogicOperator5); // complex expressions TEST_CASE(incorrectLogicOperator6); // char literals TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(memsetZeroBytes); TEST_CASE(memsetInvalid2ndParam); TEST_CASE(redundantGetAndSetUserId); TEST_CASE(clarifyCalculation); TEST_CASE(clarifyStatement); 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(incorrectStringCompare); 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: Fragement 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(alwaysTrueFalseStringCompare); TEST_CASE(suspiciousStringCompare); TEST_CASE(suspiciousStringCompare_char); TEST_CASE(checkSignOfUnsignedVariable); TEST_CASE(checkSignOfPointer); TEST_CASE(checkForSuspiciousSemicolon1); TEST_CASE(checkForSuspiciousSemicolon2); TEST_CASE(checkDoubleFree); TEST_CASE(checkInvalidFree); TEST_CASE(checkRedundantCopy); TEST_CASE(checkNegativeShift); TEST_CASE(incompleteArrayFill); TEST_CASE(redundantVarAssignment); TEST_CASE(redundantMemWrite); TEST_CASE(varFuncNullUB); TEST_CASE(checkPipeParameterSize); // ticket #3521 TEST_CASE(checkCastIntToCharAndBack); // ticket #160 TEST_CASE(checkSleepTimeIntervall) TEST_CASE(checkCommaSeparatedReturn); TEST_CASE(checkComparisonFunctionIsAlwaysTrueOrFalse); TEST_CASE(integerOverflow) // #5895 } void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool posix = false, bool runSimpleChecks=true, Settings* settings = 0) { // Clear the error buffer.. errout.str(""); if (!settings) { static Settings _settings; settings = &_settings; } settings->addEnabled("style"); settings->addEnabled("warning"); settings->addEnabled("portability"); settings->addEnabled("performance"); settings->inconclusive = inconclusive; settings->experimental = experimental; settings->standards.posix = posix; if (posix) { const char cfg[] = "\n" "\n" " 0:999999 \n" ""; tinyxml2::XMLDocument xmldoc; xmldoc.Parse(cfg, sizeof(cfg)); settings->library.load(xmldoc); } // 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) { const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); if (str1 != str2) warn(("Unsimplified code in test case\nstr1="+str1+"\nstr2="+str2).c_str()); checkOther.runSimplifiedChecks(&tokenizer, settings, this); } } 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; }; void check_preprocess_suppress(const char precode[], const char *filename = nullptr) { // Clear the error buffer.. errout.str(""); if (filename == nullptr) filename = "test.cpp"; Settings settings; settings.addEnabled("warning"); settings.addEnabled("style"); settings.addEnabled("performance"); settings.experimental = true; // Preprocess file.. SimpleSuppressor logger(settings, this); Preprocessor preprocessor(&settings, &logger); std::list configurations; std::string filedata = ""; std::istringstream fin(precode); preprocessor.preprocess(fin, filedata, configurations, filename, settings._includePaths); const std::string code = preprocessor.getcode(filedata, "", filename); // Tokenize.. Tokenizer tokenizer(&settings, &logger); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // Check.. CheckOther checkOther(&tokenizer, &settings, &logger); checkOther.checkSwitchCaseFallThrough(); checkOther.checkAlwaysTrueOrFalseStringCompare(); logger.reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(filename)); } 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 conditions in nested 'if' blocks lead 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 conditions in nested 'if' blocks lead 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 conditions in nested 'if' blocks lead 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 conditions in nested 'if' blocks lead 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 conditions in nested 'if' blocks lead to a dead code block.\n", 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 conditions in nested 'if' blocks lead to a dead code block.\n", errout.str()); 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()); // #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()); // #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()); } void emptyBrackets() { check("{\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv1() { check("void foo() {\n" " cout << 1. / 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< 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>0' is useless 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 useless 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 useless 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 useless 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 useless 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 useless 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()); // ?: check("int f(int d) {\n" " int r = (a?b:c) / d;\n" " if (d == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'd==0' is useless or there is division by zero at line 2.\n", 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\n\", 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\n\", 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\n\", 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\n\", 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\n\", x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void invalidFunctionUsage(const char code[]) { // Clear the error buffer.. errout.str(""); const char cfg[] = "\n" "\n" " \n" " 0,2:36 \n" ""; tinyxml2::XMLDocument xmldoc; xmldoc.Parse(cfg, sizeof(cfg)); Settings settings; settings.library.load(xmldoc); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for redundant code.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.invalidFunctionUsage(); } void invalidFunctionUsage1() { invalidFunctionUsage("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()); invalidFunctionUsage("int f() { memset(a,b,sizeof(a)!=0); }"); TODO_ASSERT_EQUALS("error", "", errout.str()); invalidFunctionUsage("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 (comparison result) but the valid values are '0,2:36'.\n", errout.str()); invalidFunctionUsage("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()); invalidFunctionUsage("int f() { strtol(a,b,10); }"); ASSERT_EQUALS("", errout.str()); } void sprintf1() { invalidFunctionUsage("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() { invalidFunctionUsage("void foo()\n" "{\n" " char buf[100];\n" " sprintf(buf,\"%i\",sizeof(buf));\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf3() { invalidFunctionUsage("void foo()\n" "{\n" " char buf[100];\n" " sprintf(buf,\"%i\",sizeof(buf));\n" " if (buf[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void sprintf4() { invalidFunctionUsage("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 strPlusChar(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for redundant code.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.strPlusChar(); } void strPlusChar1() { // Strange looking pointer arithmetic.. strPlusChar("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.. strPlusChar("void foo()\n" "{\n" " char ch = 1;\n" " const char *p = ch + \"/usr\";\n" "}"); ASSERT_EQUALS("", errout.str()); // Strange looking pointer arithmetic.. strPlusChar("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.. strPlusChar("void foo()\n" "{\n" " std::string temp = \"/tmp\";\n" " std::string path = temp + '/' + \"sub\" + '/';\n" "}"); ASSERT_EQUALS("", errout.str()); } void strPlusChar4() { // don't crash strPlusChar("int test() { int +; }"); } void varScope(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for redundant code.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkVariableScope(); } void varScope1() { varScope("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() { varScope("int foo()\n" "{\n" " Error e;\n" " e.SetValue(12);\n" " throw e;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope3() { varScope("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() { varScope("void foo()\n" "{\n" " int i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope5() { varScope("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()); varScope("void f(int x) {\n" " const unsigned char i = 0;\n" " if (x) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("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() { varScope("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()); varScope("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()); varScope("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() { varScope("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() { varScope("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 varScope("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() { varScope("int f()\n" "{\n" " int x = 0;\n" " FOR {\n" " foo(x++);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope11() { varScope("int f() {\n" " int x = 0;\n" " AB ab = { x, 0 };\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("int f() {\n" " int x = 0;\n" " if (a == 0) { ++x; }\n" " AB ab = { x, 0 };\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("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() { varScope("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()); varScope("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()); varScope("void f(int x) {\n" " const bool b = true;\n" " x++;\n" " if (x == 5)\n" " foo(b);\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("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 varScope("void f() {\n" " int i = 0;\n" " forever {\n" " if (i++ == 42) { break; }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope14() { // #3941 varScope("void f() {\n" " const int i( foo());\n" " if(a) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope15() { // #4573 varScope("void f() {\n" " int a,b,c;\n" " if (a);\n" " else if(b);\n" " else if(c);\n" " else;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope16() { varScope("void f() {\n" " int a = 0;\n" " while((++a) < 56) {\n" " foo();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("void f() {\n" " int a = 0;\n" " do {\n" " foo();\n" " } while((++a) < 56);\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("void f() {\n" " int a = 0;\n" " do {\n" " a = 64;\n" " foo(a);\n" " } while((++a) < 56);\n" "}"); ASSERT_EQUALS("", errout.str()); varScope("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() { varScope("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()); varScope("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() { varScope("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()); varScope("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()); varScope("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()); varScope("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 varScope19() { // Ticket #4994 varScope("long f () {\n" " return a >> extern\n" "}\n" "long a = 1 ;\n" "long b = 2 ;"); ASSERT_EQUALS("", errout.str()); } void varScope20() { // Ticket #5103 - constant variable only used in inner scope varScope("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 varScope("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. varScope("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: varScope("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 checkOldStylePointerCast(const char code[]) { // Clear the error buffer.. errout.str(""); 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"); Tokenizer tokenizerC(&settings, this); std::istringstream istr2(code); tokenizerC.tokenize(istr2, "test.c"); CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); checkOtherCpp.warningOldStylePointerCast(); CheckOther checkOtherC(&tokenizerC, &settings, this); checkOtherC.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 *) ( 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()); // #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()); } void checkInvalidPointerCast(const char code[], bool portability = false, 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]: (warning) Casting between float* and double* which have an incompatible binary data representation.\n" "[test.cpp:4]: (warning) Casting between float* and long double* which have an incompatible binary data representation.\n", "[test.cpp:3]: (warning) Casting between float* and double* which have an incompatible binary data representation.\n" "[test.cpp:4]: (warning) 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]: (warning) Casting between 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]: (warning) Casting between double* and long double* which have an incompatible binary data representation.\n" "[test.cpp:3]: (warning) 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]: (warning) Casting between integer* and long double* which have an incompatible binary data representation.\n" "[test.cpp:3]: (warning) Casting between integer* and double* which have an incompatible binary data representation.\n" "[test.cpp:4]: (warning) 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]: (warning) 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]: (warning) 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 testPassedByValue(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("performance"); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkConstantFunctionParameter(); } void passedByValue() { testPassedByValue("void f(const std::string str) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::string::size_type x) {}"); ASSERT_EQUALS("", errout.str()); testPassedByValue("class Foo;\nvoid f(const Foo foo) {}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::string &str) {}"); ASSERT_EQUALS("", errout.str()); testPassedByValue("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::vector::size_type s) {}"); ASSERT_EQUALS("", errout.str()); testPassedByValue("void f(const std::vector &v) {}"); ASSERT_EQUALS("", errout.str()); testPassedByValue("void f(const std::map &v) {}"); ASSERT_EQUALS("", errout.str()); testPassedByValue("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by reference.\n", errout.str()); testPassedByValue("void f(const std::streamoff pos) {}"); 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]: (error) Passing value -1 to sqrt() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -1 to sqrtf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -1 to sqrtl() leads to undefined result.\n", 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]: (error) Passing value -2 to log() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -2 to logf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -2 to logl() leads to undefined 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]: (error) Passing value -1 to log() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -1 to logf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -1 to logl() leads to undefined 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]: (error) Passing value -1.0 to log() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -1.0 to logf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -1.0 to logl() leads to undefined 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]: (error) Passing value -0.1 to log() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -0.1 to logf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -0.1 to logl() leads to undefined 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]: (error) Passing value 0 to log() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value 0. to logf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value 0.0 to logl() leads to undefined 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]: (error) Passing value 1.1 to acos() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value 1.1 to acosf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value 1.1 to acosl() leads to undefined 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]: (error) Passing value -1.1 to acos() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -1.1 to acosf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -1.1 to acosl() leads to undefined 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]: (error) Passing value 1.1 to asin() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value 1.1 to asinf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value 1.1 to asinl() leads to undefined 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]: (error) Passing value -1.1 to asin() leads to undefined result.\n" "[test.cpp:4]: (error) Passing value -1.1 to asinf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing value -1.1 to asinl() leads to undefined 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]: (error) Passing values 0 and -10 to pow() leads to undefined result.\n" "[test.cpp:4]: (error) Passing values 0 and -10 to powf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing values 0 and -10 to powl() leads to undefined 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) << std::endl;\n" " std::cout << atan2(-1,-1) << std::endl;\n" " std::cout << atan2(0.1,1) << std::endl;\n" " std::cout << atan2(0.0001,100) << std::endl;\n" " std::cout << atan2(0.01m-1) << std::endl;\n" " std::cout << atan2(1.0E-1,-3) << std::endl;\n" " std::cout << atan2(-1.0E-1,+2) << std::endl;\n" " std::cout << atan2(+1.0E-1,0) << std::endl;\n" " std::cout << atan2(0.1E-1,3) << std::endl;\n" " std::cout << atan2(+0.1E-1,1) << std::endl;\n" " std::cout << atan2(-0.1E-1,8) << std::endl;\n" " std::cout << atan2f(1,1) << std::endl;\n" " std::cout << atan2f(-1,-1) << std::endl;\n" " std::cout << atan2f(0.1,1) << std::endl;\n" " std::cout << atan2f(0.0001,100) << std::endl;\n" " std::cout << atan2f(0.01m-1) << std::endl;\n" " std::cout << atan2f(1.0E-1,-3) << std::endl;\n" " std::cout << atan2f(-1.0E-1,+2) << std::endl;\n" " std::cout << atan2f(+1.0E-1,0) << std::endl;\n" " std::cout << atan2f(0.1E-1,3) << std::endl;\n" " std::cout << atan2f(+0.1E-1,1) << std::endl;\n" " std::cout << atan2f(-0.1E-1,8) << std::endl;\n" " std::cout << atan2l(1,1) << std::endl;\n" " std::cout << atan2l(-1,-1) << std::endl;\n" " std::cout << atan2l(0.1,1) << std::endl;\n" " std::cout << atan2l(0.0001,100) << std::endl;\n" " std::cout << atan2l(0.01m-1) << std::endl;\n" " std::cout << atan2l(1.0E-1,-3) << std::endl;\n" " std::cout << atan2l(-1.0E-1,+2) << std::endl;\n" " std::cout << atan2l(+1.0E-1,0) << std::endl;\n" " std::cout << atan2l(0.1E-1,3) << std::endl;\n" " std::cout << atan2l(+0.1E-1,1) << std::endl;\n" " std::cout << atan2l(-0.1E-1,8) << std::endl;\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]: (error) Passing values 0 and 0 to atan2() leads to undefined result.\n" "[test.cpp:4]: (error) Passing values 0 and 0 to atan2f() leads to undefined result.\n" "[test.cpp:5]: (error) Passing values 0 and 0 to atan2l() leads to undefined 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]: (error) Passing values 1.0 and 0 to fmod() leads to undefined result.\n" "[test.cpp:4]: (error) Passing values 1.0 and 0 to fmodf() leads to undefined result.\n" "[test.cpp:5]: (error) Passing values 1.0 and 0 to fmodl() leads to undefined 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 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(char *str, int a)\n" "{\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " case 3:\n" " strcpy(str, \"b'\");\n" " }\n" "}", 0, false, 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(char *str, int a)\n" "{\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(char *str, int a)\n" "{\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" "}", 0, false, 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(char *str, int a)\n" "{\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(char *str, int a)\n" "{\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " printf(str);\n" " case 3:\n" " strcpy(str, \"b'\");\n" " }\n" "}", 0, false, 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", 0, false, false, false, false); 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" "}", 0, false, 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" "}", 0, false, 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 switchFallThroughCase() { check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " break;\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " break;\n" " case 2:\n" " continue;\n" " case 3:\n" " return;\n" " case 4:\n" " exit(1);\n" " case 5:\n" " goto end;\n" " case 6:\n" " throw e;\n" " case 7:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 0:\n" " case 1:\n" " break;\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " default:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " // fall through\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " /* FALLTHRU */\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " break;\n" " // fall through\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (information) Unmatched suppression: switchCaseFallThrough\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " {\n" " break;\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " for (;;) {\n" " break;\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " if (b) {\n" " break;\n" " } else {\n" " break;\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " if (b) {\n" " break;\n" " } else {\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " if (b) {\n" " break;\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " if (b) {\n" " } else {\n" " break;\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " if (b) {\n" " case 2:\n" " } else {\n" " break;\n" " }\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " int x;\n" " case 1:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " switch (b) {\n" " case 1:\n" " return;\n" " default:\n" " return;\n" " }\n" " case 2:\n" " break;\n" " }\n" "}"); // This fails because the switch parsing code currently doesn't understand // that all paths after g() actually return. It's a pretty unusual case // (no pun intended). TODO_ASSERT_EQUALS("", "[test.cpp:11]: (style) Switch falls through case without comment. 'break;' missing?\n", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" "#ifndef A\n" " g();\n" " // fall through\n" "#endif\n" " case 2:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " goto leave;\n" " case 2:\n" " break;\n" " }\n" "leave:\n" " if (x) {\n" " g();\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " switch (a) {\n" " case 1:\n" " g();\n" " // fall through\n" " case 2:\n" " g();\n" " // falls through\n" " case 3:\n" " g();\n" " // fall-through\n" " case 4:\n" " g();\n" " // drop through\n" " case 5:\n" " g();\n" " // pass through\n" " case 5:\n" " g();\n" " // no break\n" " case 5:\n" " g();\n" " // fallthru\n" " case 6:\n" " g();\n" " /* fall */\n" " default:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "void foo() {\n" " // unrelated comment saying 'fall through'\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" "}", 0, false, 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" "}", 0, false, 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" "}", 0, false, 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); check("void foo() {\n" " exit(0);\n" " break;\n" "}", 0, false, false, false, false, &settings); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("class NeonSession {\n" " void exit();\n" "};\n" "void NeonSession::exit()\n" "{\n" " SAL_INFO(\"ucb.ucp.webdav\", \"neon commands cannot be aborted\");\n" "}", 0, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void NeonSession::exit()\n" "{\n" " SAL_INFO(\"ucb.ucp.webdav\", \"neon commands cannot be aborted\");\n" "}", 0, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() { xResAccess->exit(); }", 0, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " switch(a) {\n" " case 0:\n" " printf(\"case 0\");\n" " break;\n" " break;\n" " case 1:\n" " c++;\n" " break;\n" " }\n" "}", 0, false, 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" "}", 0, false, 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" "}", 0, false, 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" "}", 0, false, false, false, false); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " throw 0;\n" " return;\n" "}", 0, false, 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" "}", 0, false, 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" "}", 0, false, 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() {\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" "}", 0, false, 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" "}", 0, false, 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" "}", 0, false, 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" "}", 0, false, false, false, false); ASSERT_EQUALS("", 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 check("%: return ; ()"); // Don't crash. #3441. // #3383. TODO: Use preprocessor check("int foo() {\n" "\n" // #ifdef A " return 0;\n" "\n" // #endif " return 1;\n" "}", 0, false, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" "\n" // #ifdef A " return 0;\n" "\n" // #endif " return 1;\n" "}", 0, false, true, false, 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" "}", 0, false, 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" "}", 0, false, false, false, false); 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\n\", ({x==0;}));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " printf(\"%i\n\", ({int x = do_something(); x == 0;}));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " printf(\"%i\n\", ({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("void foo()\n" "{\n" " std::string var = var = \"test\";\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'var' 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()); } 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]: (error) 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", 0, false, 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]: (error) 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]: (error) 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]: (error) 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]: (error) 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]: (error) 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 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 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" // 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" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}\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 == 3)\n" " a++;\n" "}\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" "}\n" ); //ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1.0 && x == 3.0.\n", errout.str()); 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" "}\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" "}\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" "}\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" "}\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" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}\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" "}\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" "}\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" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 10)\n" " a++;\n" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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" "}\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]: (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" "}\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("void f(int x) {\n" " if (x && x != $0) {}\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" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char x) {\n" " if (x == '1' && x == '2') {}\n" "}"); TODO_ASSERT_EQUALS("error", "", 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 memsetZeroBytes() { check("void f() {\n" " memset(p, 10, 0x0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes of 'p'.\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 of 'p'.\n", errout.str()); check("void f() {\n" " memset(p, sizeof(p), i);\n" "}\n"); ASSERT_EQUALS("", 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()); } void redundantGetAndSetUserId() { check("seteuid(geteuid());\n", nullptr, false , false, true); ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant get and set of user id.\n", errout.str()); check("setuid(getuid());\n", nullptr, false , false, true); ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant get and set of user id.\n", errout.str()); check("setgid(getgid());\n", nullptr, false , false, true); ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant get and set of user id.\n", errout.str()); check("setegid(getegid());\n", nullptr, false , false, true); ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant get and set of user id.\n", errout.str()); check("seteuid(getuid());\n", nullptr, false , false, true); ASSERT_EQUALS("", errout.str()); check("seteuid(foo());\n", nullptr, false , false, true); ASSERT_EQUALS("", errout.str()); check("foo(getuid());\n", nullptr, false , false, true); 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()); // Ticket #2585 - segmentation fault for invalid code check("abcdef?""?<" "123456?""?>" "+?""?="); 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,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()); // 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()); // #5166 segmentation fault (invalid code) in lib/checkother.cpp:329 ( void * f { } void b ( ) { * f } ) check("void * f { } void b ( ) { * f }"); 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 "}", 0, false, false, false, false); 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()); } // 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", 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() {\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(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.C"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (result != (char *)&inline_result) { }\n" // don't simplify and verify cast "}", 0, false, false, false, false); 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 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 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" "}", 0, false, 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: Fragement 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()); } void duplicateBranch2() { Preprocessor::macroChar = '$'; check("void f(int x) {\n" // #4329 " if (x)\n" " $;\n" " else\n" " $;\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() {\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 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, // posix 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() {\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() {\n" " if ((b > a) | (a > b)) {}\n" // > is not commutative "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\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()); } 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, 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()); // #5284 - when type is unknown, assume it's float check("int f() { return x==x; }"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression3() { 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()); { Settings settings; LOAD_LIB_2(settings.library, "std.cfg"); check("void foo() {\n" " if ((strcmp(a, b) == 0) || (strcmp(a, b) == 0)) {}\n" "}", "test.cpp", false, 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, 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 Preprocessor::macroChar = '$'; 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 alwaysTrueFalseStringCompare() { check_preprocess_suppress( "#define MACRO \"00FF00\"\n" "int main()\n" "{\n" " if (strcmp(MACRO,\"00FF00\") == 0)" " {" " std::cout << \"Equal\n\"" " }" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check_preprocess_suppress( "int main()\n" "{\n" " if (stricmp(\"hotdog\",\"HOTdog\") == 0)" " {" " std::cout << \"Equal\n\"" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check_preprocess_suppress( "#define MACRO \"Hotdog\"\n" "int main()\n" "{\n" " if (QString::compare(\"Hamburger\", MACRO) == 0)" " {" " std::cout << \"Equal\n\"" " }" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check_preprocess_suppress( "int main()\n" "{\n" " if (QString::compare(argv[2], \"hotdog\") == 0)" " {" " std::cout << \"Equal\n\"" " }" "}"); ASSERT_EQUALS("", errout.str()); check_preprocess_suppress( "int main()\n" "{\n" " if (strncmp(\"hotdog\",\"hotdog\", 6) == 0)" " {" " std::cout << \"Equal\n\"" " }" "}"); 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\n\"" " }" "}"); 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\n\"" " }" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of identical string variables.\n", errout.str()); check_preprocess_suppress( "int main() {\n" " if (\"str\" == \"str\") {\n" " std::cout << \"Equal\n\"\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check_preprocess_suppress( "int main() {\n" " if (\"str\" != \"str\") {\n" " std::cout << \"Equal\n\"\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Unnecessary comparison of static strings.\n", errout.str()); check_preprocess_suppress( "int main() {\n" " if (a+\"str\" != \"str\"+b) {\n" " std::cout << \"Equal\n\"\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 == '42';}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("int foo(char c) {\n" "return c == '42';}", "test.c"); ASSERT_EQUALS("", errout.str()); check("int foo(char c) {\n" "return c == \"42\"[0];}", "test.cpp", false, true, false, false); ASSERT_EQUALS("", errout.str()); check("int foo(char c) {\n" "return c == \"42\"[0];}", "test.c", false, true, false, false); ASSERT_EQUALS("", errout.str()); } void suspiciousStringCompare_char() { check("bool foo(char* c) {\n" " return c == '\\0';\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" "}"); TODO_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 check_signOfUnsignedVariable(const char code[], bool inconclusive=false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for redundant code.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkSignOfUnsignedVariable(); } void checkSignOfUnsignedVariable() { check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "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_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x) {\n" " if (x < 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x) {\n" " if (0 > x)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x) {\n" " if (x >= 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (x < 0 && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (0 > x && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (x >= 0 && y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (y && x < 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (y && 0 > x)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (y && x >= 0)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (x < 0 || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int x, bool y) {\n" " if (0 > x || y)" " return true;\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "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_signOfUnsignedVariable(code, false); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable(code, 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_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int* x) {\n" " if (*x >= 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int* x) {\n" " if (*x < 0)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int* x) {\n" " if (0 <= x[0])" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "bool foo(Bar* x) {\n" " if (0 <= x.y)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "bool foo(int* x) {\n" " if (0 > x[0])" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "bool foo(Bar* x) {\n" " if (0 > x.y)" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "void foo() {\n" " int (*t)(void *a, void *b);\n" " if (t(a, b) < 0)\n" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "void foo() {\n" " int (*t)(void *a, void *b);\n" " if (0 > t(a, b))\n" " bar();\n" "}"); ASSERT_EQUALS("", errout.str()); check_signOfUnsignedVariable( "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_signOfUnsignedVariable( "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_signOfUnsignedVariable( "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 checkDoubleFree() { check( "void foo(char *p) {\n" " free(p);\n" " free(p);\n" "}"); 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" " 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.cpp: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.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(DIR *p) {\n" " closedir(p);\n" " closedir(p);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Directory handle 'p' closed twice.\n", errout.str()); check( "void foo(DIR *p, DIR *r) {\n" " closedir(p);\n" " closedir(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(DIR *p) {\n" " if (x < 3) closedir(p);\n" " else { if (x > 9) closedir(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(DIR *p) {\n" " closedir(p);\n" " gethandle(&p);\n" " closedir(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(DIR *p) {\n" " closedir(p);\n" " gethandle();\n" " closedir(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Directory handle 'p' closed 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.cpp: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" " g_free(p);\n" " g_free(p);\n" "}"); 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" " g_free(p);\n" " g_free(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " g_free(p);\n" " getNext(&p);\n" " g_free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " g_free(p);\n" " bar();\n" " g_free(p);\n" "}"); 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" "}"); 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" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " getNext(&p);\n" " delete p;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " bar();\n" " delete p;\n" "}"); 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" "}"); 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" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " getNext(&p);\n" " delete[] p;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " bar();\n" " delete[] p;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "~LineMarker() {\n" " delete pxpm;\n" "}\n" "LineMarker &operator=(const LineMarker &) {\n" " delete pxpm;\n" " pxpm = NULL;\n" " return *this;\n" "}" ); 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" "}" ); 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" "}" ); ASSERT_EQUALS("", 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" "}" ); 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" "}" ); 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" "}" ); 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.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" " for (;;) {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}" ); 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" "}" ); 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" "}" ); TODO_ASSERT_EQUALS("[test.cpp: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" "}\n" ); 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" "}\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 check_redundant_copy(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("performance"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Simplify token list.. CheckOther checkOther(&tokenizer, &settings, this); tokenizer.simplifyTokenList2(); checkOther.checkRedundantCopy(); } void checkRedundantCopy() { check_redundant_copy("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_redundant_copy("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_redundant_copy("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_redundant_copy("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_redundant_copy("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_redundant_copy("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_redundant_copy("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_redundant_copy("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_redundant_copy("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 check_redundant_copy("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" "}\n"); TODO_ASSERT_EQUALS("", "[test.cpp:7]: (performance, inconclusive) Use const reference for 'temp' to avoid unnecessary data copying.\n", errout.str()); // #5890 - crash: wesnoth desktop_util.cpp / unicode.hpp check_redundant_copy("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()); } 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: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("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]: (performance) 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]: (performance, 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, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) 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]: (performance, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\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]: (performance, 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]: (performance) 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, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) 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, 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]: (performance) 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]: (performance) 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();\n" "};\n" "\n" "void C::f(Foo z) {\n" " x = 2;\n" " x = z.g();\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:9]: (performance, inconclusive) Variable 'x' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", 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, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) 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, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i = 54;\n" " i = 0;\n" "}", 0, false, false, false, false); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (performance) 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, false); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (performance) 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]: (performance, inconclusive) Variable 'a' is reassigned a value before the old one has been used if variable is no semaphore variable.\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()); // 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" "}"); } void redundantMemWrite() { // Simple tests check("void f(void* a) {\n" " memcpy(a, foo, bar);\n" " memset(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void* a;\n" "void f() {\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" " void* a = foo();\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()); // 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(void* a) {\n" " strcpy(a, foo);\n" " strcat(a, bar);\n" // Not redundant " strcpy(a, x);\n" // Redundant "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (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(void* a) {\n" " snprintf(a, foo, bar);\n" " bar();\n" " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (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" " void* a = foo();\n" " memset(a, 0, size);\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 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()); } 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 check("void f(){\n" "int pipefd[1];\n" // <-- array of two integers is needed "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}",nullptr,false,false,true); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer 'pipefd' must have size of 2 integers if used as parameter of pipe().\n", errout.str()); check("void f(){\n" "int pipefd[2];\n" "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}",nullptr,false,false,true); ASSERT_EQUALS("", errout.str()); check("void f(){\n" "int pipefd[20];\n" "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}",nullptr,false,false,true); ASSERT_EQUALS("", errout.str()); check("void f(){\n" "int pipefd[1];\n" // <-- array of two integers is needed "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}",nullptr,false,false,true); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer 'pipefd' must have size of 2 integers if used as parameter of pipe().\n", errout.str()); check("void f(){\n" "int pipefd[2];\n" "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}",nullptr,false,false,true); ASSERT_EQUALS("", errout.str()); check("void f(){\n" "int pipefd[20];\n" "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}",nullptr,false,false,true); 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" "}\n"); ASSERT_EQUALS("", errout.str()); // test with unknown variable check("void foo {\n" " if ( pipe (cp) == -1 ) {\n" " return;\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" "}\n"); ASSERT_EQUALS("", errout.str()); // test with unknown variable check("void foo {\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\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 checkSleepTimeIntervall() { // check usleep(), which is allowed to be called with in a range of [0,999999] check("void f(){\n" "usleep(10000);\n" "}",nullptr,false,false,true); ASSERT_EQUALS("", errout.str()); check("void f(){\n" "usleep(999999);\n" "}",nullptr,false,false,true); ASSERT_EQUALS("", errout.str()); check("void f(){\n" "usleep(1000000);\n" "}",nullptr,false,false,true); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usleep() argument nr 1. The value is 1000000 but the valid values are '0:999999'.\n", errout.str()); check("void f(){\n" "usleep(1000001);\n" "}",nullptr,false,false,true); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usleep() argument nr 1. The value is 1000001 but the valid values are '0:999999'.\n", errout.str()); } void checkCommaSeparatedReturn() { check("int fun(int a) {\n" " if (a < 0)\n" " return a++,\n" " do_something();\n" "}", nullptr, true, false, 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, 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, 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, false); ASSERT_EQUALS("", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return c::b;\n" "}", nullptr, true, false, false, false); ASSERT_EQUALS("", errout.str()); // ticket #4927 Segfault in CheckOther::checkCommaSeparatedReturn() on invalid code check("int main() {\n" " return 0\n" "}", nullptr, true, false, 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, 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) evaluates always 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) evaluates always 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) evaluates always 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) evaluates always 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) evaluates always 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()); } }; REGISTER_TEST(TestOther) cppcheck-1.66/test/testpath.cpp000066400000000000000000000127031236713773000165610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "path.h" class TestPath : public TestFixture { public: TestPath() : TestFixture("TestPath") { } private: void run() { TEST_CASE(simplify_path); TEST_CASE(accept_file); TEST_CASE(getRelative); TEST_CASE(is_c); TEST_CASE(is_cpp); TEST_CASE(get_path_from_filename); } void simplify_path() const { // Path::simplifyPath() ASSERT_EQUALS("index.h", Path::simplifyPath("index.h")); ASSERT_EQUALS("index.h", Path::simplifyPath("./index.h")); ASSERT_EQUALS("/index.h", Path::simplifyPath("/index.h")); ASSERT_EQUALS("/path/", Path::simplifyPath("/path/")); ASSERT_EQUALS("/", Path::simplifyPath("/")); ASSERT_EQUALS("../index.h", Path::simplifyPath("../index.h")); ASSERT_EQUALS("/index.h", Path::simplifyPath("/path/../index.h")); ASSERT_EQUALS("/index.h", Path::simplifyPath("/path/../other/../index.h")); ASSERT_EQUALS("/index.h", Path::simplifyPath("/path/../other///././../index.h")); ASSERT_EQUALS("../path/index.h", Path::simplifyPath("../path/other/../index.h")); ASSERT_EQUALS("a/index.h", Path::simplifyPath("a/../a/index.h")); ASSERT_EQUALS("a/..", Path::simplifyPath("a/..")); ASSERT_EQUALS("../../src/test.cpp", Path::simplifyPath("../../src/test.cpp")); ASSERT_EQUALS("../../../src/test.cpp", Path::simplifyPath("../../../src/test.cpp")); // 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 accept_file() 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 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.66/test/testpathmatch.cpp000066400000000000000000000133641236713773000176020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "pathmatch.h" 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.66/test/testpostfixoperator.cpp000066400000000000000000000300721236713773000210740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkpostfixoperator.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestPostfixOperator : public TestFixture { public: TestPostfixOperator() : TestFixture("TestPostfixOperator") { } private: void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("performance"); //settings.inconclusive = true; // 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() { 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(testHangWithInvalidCode); // #2847 - cppcheck hangs with 100% cpu load TEST_CASE(testtemplate); // #4686 TEST_CASE(testmember); TEST_CASE(testcomma); } void testHangWithInvalidCode() { check("a,b--\n"); ASSERT_EQUALS("", errout.str()); } 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.66/test/testpreprocessor.cpp000066400000000000000000004501151236713773000203560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 "testsuite.h" #include "preprocessor.h" #include "tokenize.h" #include "token.h" #include "settings.h" #include #include #include #include extern std::ostringstream errout; extern std::ostringstream output; #ifdef _MSC_VER // Visual Studio complains about truncated values for '(char)0xff' and '(char)0xfe' // TODO: Is there any nice way to fix these warnings? #pragma warning( disable : 4310 ) #endif class TestPreprocessor : public TestFixture { public: TestPreprocessor() : TestFixture("TestPreprocessor") { Preprocessor::macroChar = '$'; } class OurPreprocessor : public Preprocessor { public: static std::string replaceIfDefined(const std::string &str) { Preprocessor p; return p.replaceIfDefined(str); } static std::string expandMacros(const std::string& code, ErrorLogger *errorLogger = 0) { return Preprocessor::expandMacros(code, "file.cpp", "", errorLogger); } static int getHeaderFileName(std::string &str) { return Preprocessor::getHeaderFileName(str); } }; private: void run() { // Just read the code into a string. Perform simple cleanup of the code TEST_CASE(readCode1); TEST_CASE(readCode2); // #4308 - convert C++11 raw string to plain old C string TEST_CASE(readCode3); TEST_CASE(readCode4); // #4351 - escaped whitespace in gcc // reading utf-16 file TEST_CASE(utf16); // remove comments TEST_CASE(removeComments); // The bug that started the whole work with the new preprocessor TEST_CASE(Bug2190219); TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); TEST_CASE(test4); TEST_CASE(test5); TEST_CASE(test6); TEST_CASE(test7); TEST_CASE(test7a); TEST_CASE(test7b); TEST_CASE(test7c); TEST_CASE(test7d); TEST_CASE(test7e); TEST_CASE(test8); // #if A==1 => cfg: A=1 TEST_CASE(test9); // Don't crash for invalid code TEST_CASE(test10); // Ticket #5139 TEST_CASE(error1); // #error => don't extract any code TEST_CASE(error2); // #error with extended chars TEST_CASE(error3); TEST_CASE(error4); // #2919 - wrong filename is reported TEST_CASE(error5); TEST_CASE(if0_exclude); TEST_CASE(if0_whitespace); TEST_CASE(if0_else); TEST_CASE(if0_elif); // Don't handle include in a #if 0 block TEST_CASE(if0_include_1); TEST_CASE(if0_include_2); // Handling include guards (don't create extra configuration for it) TEST_CASE(includeguard1); TEST_CASE(includeguard2); TEST_CASE(newlines); TEST_CASE(comments1); TEST_CASE(if0); TEST_CASE(if1); TEST_CASE(elif); // Test the Preprocessor::match_cfg_def TEST_CASE(match_cfg_def); 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_cond15); // #4456 - segfault 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 TEST_CASE(multiline1); TEST_CASE(multiline2); TEST_CASE(multiline3); TEST_CASE(multiline4); TEST_CASE(multiline5); TEST_CASE(remove_asm); TEST_CASE(if_defined); // "#if defined(AAA)" => "#ifdef AAA" TEST_CASE(if_not_defined); // "#if !defined(AAA)" => "#ifndef AAA" // 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_mismatch); TEST_CASE(macro_linenumbers); TEST_CASE(macro_nopar); 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); TEST_CASE(va_args_3); TEST_CASE(va_args_4); 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(pragma_once); TEST_CASE(endifsemicolon); TEST_CASE(missing_doublequote); TEST_CASE(handle_error); TEST_CASE(dup_defines); TEST_CASE(unicodeInCode); TEST_CASE(unicodeInComment); TEST_CASE(unicodeInString); TEST_CASE(define_part_of_func); TEST_CASE(conditionalDefine); TEST_CASE(multiline_comment); TEST_CASE(macro_parameters); TEST_CASE(newline_in_macro); TEST_CASE(includes); 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(testPreprocessorRead1); TEST_CASE(testPreprocessorRead2); TEST_CASE(testPreprocessorRead3); TEST_CASE(testPreprocessorRead4); TEST_CASE(invalid_define_1); // #2605 - hang for: '#define =' TEST_CASE(invalid_define_2); // #4036 - hang for: '#define () {(int f(x) }' // Show 'missing include' warnings TEST_CASE(missingInclude); // 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(predefine6); // using -D and -f => check all matching configurations // Test Preprocessor::simplifyCondition TEST_CASE(simplifyCondition); TEST_CASE(invalidElIf); // #2942 segfault // Defines are given: test Preprocessor::handleIncludes TEST_CASE(def_handleIncludes); TEST_CASE(def_missingInclude); TEST_CASE(def_handleIncludes_ifelse1); // problems in handleIncludes for #else TEST_CASE(def_handleIncludes_ifelse2); TEST_CASE(def_handleIncludes_ifelse3); // #4868 - crash TEST_CASE(def_valueWithParentheses); // #3531 // Using -U to undefine symbols TEST_CASE(undef1); TEST_CASE(undef2); TEST_CASE(undef3); TEST_CASE(undef4); TEST_CASE(undef5); TEST_CASE(undef6); TEST_CASE(undef7); TEST_CASE(undef8); TEST_CASE(undef9); TEST_CASE(undef10); TEST_CASE(handleUndef); TEST_CASE(macroChar); TEST_CASE(validateCfg); TEST_CASE(if_sizeof); TEST_CASE(double_include); // #5717 TEST_CASE(invalid_ifs)// #5909 } void readCode1() { const char code[] = " \t a //\n" " #aa\t /* remove this */\tb \r\n"; Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::string codestr(preprocessor.read(istr,"test.c")); ASSERT_EQUALS("a\n#aa b\n", codestr); } void readCode2() { const char code[] = "R\"( \" /* abc */ \n)\";"; Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::string codestr(preprocessor.read(istr,"test.c")); ASSERT_EQUALS("\" \\\" /* abc */ \\n\"\n;", codestr); } void readCode3() { const char code[] = "func(#errorname)"; Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::string codestr(preprocessor.read(istr,"test.c")); ASSERT_EQUALS("func(#errorname)", codestr); } void readCode4() { const char code[] = "char c = '\\ ';"; Settings settings; errout.str(""); Preprocessor preprocessor(&settings, this); std::istringstream istr(code); ASSERT_EQUALS("char c = '\\ ';", preprocessor.read(istr,"test.c")); ASSERT_EQUALS("", errout.str()); } void utf16() { Settings settings; Preprocessor preprocessor(&settings, this); // a => a { const char code[] = { (char)0xff, (char)0xfe, 'a', '\0' }; std::string s(code, sizeof(code)); std::istringstream istr(s); ASSERT_EQUALS("a", preprocessor.read(istr, "test.c")); } { const char code[] = { (char)0xfe, (char)0xff, '\0', 'a' }; std::string s(code, sizeof(code)); std::istringstream istr(s); ASSERT_EQUALS("a", preprocessor.read(istr, "test.c")); } // extended char => 0xff { const char code[] = { (char)0xff, (char)0xfe, 'a', 'a' }; std::string s(code, sizeof(code)); std::istringstream istr(s); const char expected[] = { (char)0xff, 0 }; ASSERT_EQUALS(expected, preprocessor.read(istr, "test.c")); } { const char code[] = { (char)0xfe, (char)0xff, 'a', 'a' }; std::string s(code, sizeof(code)); std::istringstream istr(s); const char expected[] = { (char)0xff, 0 }; ASSERT_EQUALS(expected, preprocessor.read(istr, "test.c")); } // \r\n => \n { const char code[] = { (char)0xff, (char)0xfe, '\r', '\0', '\n', '\0' }; std::string s(code, sizeof(code)); std::istringstream istr(s); ASSERT_EQUALS("\n", preprocessor.read(istr, "test.c")); } { const char code[] = { (char)0xfe, (char)0xff, '\0', '\r', '\0', '\n' }; std::string s(code, sizeof(code)); std::istringstream istr(s); ASSERT_EQUALS("\n", preprocessor.read(istr, "test.c")); } } void removeComments() { Settings settings; Preprocessor preprocessor(&settings, this); // #3837 - asm comments const char code[] = "void test(void) {\n" " __asm\n" " {\n" " ;---- тест\n" " }\n" "}\n"; ASSERT_EQUALS(true, std::string::npos == preprocessor.removeComments(code, "3837.c").find("----")); ASSERT_EQUALS(" __asm123", preprocessor.removeComments(" __asm123", "3837.cpp")); ASSERT_EQUALS("\" __asm { ; } \"", preprocessor.removeComments("\" __asm { ; } \"", "3837.cpp")); ASSERT_EQUALS("__asm__ volatile { \"\" }", preprocessor.removeComments("__asm__ volatile { \"\" }", "3837.cpp")); // #4873 ASSERT_EQUALS("__asm { }", preprocessor.removeComments("__asm { /* This is a comment */ }", "4873.cpp")); // #5169 ASSERT_EQUALS("#define A(B) __asm__(\"int $3\"); int wait=1;\n", preprocessor.removeComments("#define A(B) __asm__(\"int $3\"); /**/ int wait=1;\n", "5169.c")); } void Bug2190219() { const char filedata[] = "int main()\n" "{\n" "#ifdef __cplusplus\n" " int* flags = new int[10];\n" "#else\n" " int* flags = (int*)malloc((10)*sizeof(int));\n" "#endif\n" "\n" "#ifdef __cplusplus\n" " delete [] flags;\n" "#else\n" " free(flags);\n" "#endif\n" "}\n"; // Expected result.. std::map expected; expected[""] = "int main()\n" "{\n" "\n" "\n" "\n" "int* flags = (int*)malloc((10)*sizeof(int));\n" "\n" "\n" "\n" "\n" "\n" "free(flags);\n" "\n" "}\n"; expected["__cplusplus"] = "int main()\n" "{\n" "\n" "int* flags = new int[10];\n" "\n" "\n" "\n" "\n" "\n" "delete [] flags;\n" "\n" "\n" "\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(expected[""], actual[""]); ASSERT_EQUALS(expected["__cplusplus"], actual["__cplusplus"]); ASSERT_EQUALS(2, static_cast(actual.size())); } void test1() { const char filedata[] = "#ifdef WIN32 \n" " abcdef\n" "#else \n" " qwerty\n" "#endif \n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\nqwerty\n\n", actual[""]); ASSERT_EQUALS("\nabcdef\n\n\n\n", actual["WIN32"]); ASSERT_EQUALS(2, static_cast(actual.size())); } void test2() { const char filedata[] = "# ifndef WIN32\n" " \" # ifdef WIN32\" // a comment\n" " # else \n" " qwerty\n" " # endif \n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\" # ifdef WIN32\"\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\n\nqwerty\n\n", actual["WIN32"]); ASSERT_EQUALS(2, static_cast(actual.size())); } void test3() { const char filedata[] = "#ifdef ABC\n" "a\n" "#ifdef DEF\n" "b\n" "#endif\n" "c\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\na\n\n\n\nc\n\n", actual["ABC"]); ASSERT_EQUALS("\na\n\nb\n\nc\n\n", actual["ABC;DEF"]); ASSERT_EQUALS(3, static_cast(actual.size())); } void test4() { const char filedata[] = "#ifdef ABC\n" "A\n" "#endif\t\n" "#ifdef ABC\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\nA\n\n\nA\n\n", actual["ABC"]); ASSERT_EQUALS(2, static_cast(actual.size())); } void test5() { const char filedata[] = "#ifdef ABC\n" "A\n" "#else\n" "B\n" "#ifdef DEF\n" "C\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\nB\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\nA\n\n\n\n\n\n\n", actual["ABC"]); ASSERT_EQUALS("\n\n\nB\n\nC\n\n\n", actual["DEF"]); ASSERT_EQUALS(3, static_cast(actual.size())); } void test6() { const char filedata[] = "#if(A)\n" "#if ( A ) \n" "#if A\n" "#if defined((A))\n" "#elif defined (A)\n"; std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); const std::string actual(preprocessor.read(istr, "test.c")); // Compare results.. ASSERT_EQUALS("#if A\n#if A\n#if A\n#if defined(A)\n#elif defined(A)\n", actual); } void test7() { const char filedata[] = "#ifdef ABC\n" "A\n" "#ifdef ABC\n" "B\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Make sure an error message is written.. TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed to be defined\n", "", errout.str()); // Compare results.. ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\nA\n\nB\n\n\n", actual["ABC"]); ASSERT_EQUALS(2, static_cast(actual.size())); test7a(); test7b(); test7c(); test7d(); } void test7a() { const char filedata[] = "#ifndef ABC\n" "A\n" "#ifndef ABC\n" "B\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Make sure an error message is written.. TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed NOT to be defined\n", "", errout.str()); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); } void test7b() { const char filedata[] = "#ifndef ABC\n" "A\n" "#ifdef ABC\n" "B\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Make sure an error message is written.. TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed NOT to be defined\n", "", errout.str()); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); } void test7c() { const char filedata[] = "#ifdef ABC\n" "A\n" "#ifndef ABC\n" "B\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Make sure an error message is written.. TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed to be defined\n", "", errout.str()); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); } void test7d() { const char filedata[] = "#if defined(ABC)\n" "A\n" "#if defined(ABC)\n" "B\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Make sure an error message is written.. TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed to be defined\n", "", errout.str()); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); } void test7e() { 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"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Make sure an error message is written.. ASSERT_EQUALS("", errout.str()); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); } void test8() { const char filedata[] = "#if A == 1\n" "1\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // No error.. ASSERT_EQUALS("", errout.str()); // Compare results.. ASSERT_EQUALS(2U, actual.size()); ASSERT_EQUALS("\n\n\n", actual[""]); ASSERT_EQUALS("\n1\n\n", actual["A=1"]); } void test9() { const char filedata[] = "#if\n" "#else\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; settings._maxConfigs = 1; settings.userDefines = "X"; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // <- don't crash } void test10() { // Ticket #5139 const char filedata[] = "#define foo a.foo\n" "#define bar foo\n" "#define baz bar+0\n" "#if 0\n" "#endif"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); } void error1() { const char filedata[] = "#ifdef A\n" ";\n" "#else\n" "#error abcd\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); errout.str(""); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("\n;\n\n\n\n", actual["A"]); } void error2() { errout.str(""); const char filedata[] = "#error \xAB\n" "#warning \xAB\n" "123"; // Read string.. std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("#error\n\n123", preprocessor.read(istr,"test.c")); } 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 if0_exclude() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream code("#if 0\n" "A\n" "#endif\n" "B\n"); ASSERT_EQUALS("#if 0\n\n#endif\nB\n", preprocessor.read(code,"")); std::istringstream code2("#if (0)\n" "A\n" "#endif\n" "B\n"); ASSERT_EQUALS("#if 0\n\n#endif\nB\n", preprocessor.read(code2,"")); } void if0_whitespace() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream code(" # if 0 \n" "A\n" " # endif \n" "B\n"); ASSERT_EQUALS("#if 0\n\n#endif\nB\n", preprocessor.read(code,"")); } void if0_else() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream code("#if 0\n" "A\n" "#else\n" "B\n" "#endif\n" "C\n"); ASSERT_EQUALS("#if 0\n\n#else\nB\n#endif\nC\n", preprocessor.read(code,"")); std::istringstream code2("#if 1\n" "A\n" "#else\n" "B\n" "#endif\n" "C\n"); TODO_ASSERT_EQUALS("#if 1\nA\n#else\n\n#endif\nC\n", "#if 1\nA\n#else\nB\n#endif\nC\n", preprocessor.read(code2,"")); } void if0_elif() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream code("#if 0\n" "A\n" "#elif 1\n" "B\n" "#endif\n" "C\n"); ASSERT_EQUALS("#if 0\n\n#elif 1\nB\n#endif\nC\n", preprocessor.read(code,"")); } void if0_include_1() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream code("#if 0\n" "#include \"a.h\"\n" "#endif\n" "AB\n"); ASSERT_EQUALS("#if 0\n\n#endif\nAB\n", preprocessor.read(code,"")); } void if0_include_2() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream code("#if 0\n" "#include \"a.h\"\n" "#ifdef WIN32\n" "#else\n" "#endif\n" "#endif\n" "AB\n"); ASSERT_EQUALS("#if 0\n\n#ifdef WIN32\n#else\n#endif\n#endif\nAB\n", preprocessor.read(code,"")); } 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"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Expected configurations: "" and "ABC" ASSERT_EQUALS(2, static_cast(actual.size())); } void includeguard2() { // Handling include guards.. const char filedata[] = "#file \"abc.h\"\n" "foo\n" "#ifdef ABC\n" "\n" "#endif\n" "#endfile\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Expected configurations: "" and "ABC" ASSERT_EQUALS(2, static_cast(actual.size())); ASSERT_EQUALS(true, actual.find("") != actual.end()); ASSERT_EQUALS(true, actual.find("ABC") != actual.end()); } 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Expected configurations: "" and "ABC" ASSERT_EQUALS(2, static_cast(actual.size())); ASSERT_EQUALS("\n#file \"abc.h\"\n\n\n\n\n\n\n\n\n#endfile\n\nint main() {}\n", actual[""]); ASSERT_EQUALS("\n#file \"abc.h\"\nclass A{};\n\n\n\n\n\n\n\n#endfile\n\nint main() {}\n", actual["ABC"]); } void newlines() { const char filedata[] = "\r\r\n\n"; // Preprocess std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("\n\n\n", preprocessor.read(istr, "test.c")); } void comments1() { { const char filedata[] = "/*\n" "#ifdef WIN32\n" "#endif\n" "*/\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n", actual[""]); ASSERT_EQUALS(1, static_cast(actual.size())); } { const char filedata[] = "/*\n" "\x080 #ifdef WIN32\n" "#endif\n" "*/\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n", actual[""]); ASSERT_EQUALS(1, static_cast(actual.size())); } { const char filedata[] = "void f()\n" "{\n" " *p = a / *b / *c;\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("void f()\n{\n*p = a / *b / *c;\n}\n", actual[""]); ASSERT_EQUALS(1, static_cast(actual.size())); } } void if0() { const char filedata[] = " # if /* comment */ 0 // comment\n" "#ifdef WIN32\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n", actual[""]); ASSERT_EQUALS(1, static_cast(actual.size())); } void if1() { const char filedata[] = " # if /* comment */ 1 // comment\n" "ABC\n" " # endif \n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\nABC\n\n", actual[""]); ASSERT_EQUALS(1, static_cast(actual.size())); } void elif() { { const char filedata[] = "#if DEF1\n" "ABC\n" "#elif DEF2\n" "DEF\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\nABC\n\n\n\n", actual["DEF1"]); ASSERT_EQUALS("\n\n\nDEF\n\n", actual["DEF2"]); ASSERT_EQUALS(3, static_cast(actual.size())); } { const char filedata[] = "#if(defined DEF1)\n" "ABC\n" "#elif(defined DEF2)\n" "DEF\n" "#else\n" "GHI\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n\nGHI\n\n", actual[""]); ASSERT_EQUALS("\nABC\n\n\n\n\n\n", actual["DEF1"]); ASSERT_EQUALS("\n\n\nDEF\n\n\n\n", actual["DEF2"]); ASSERT_EQUALS(3, static_cast(actual.size())); } } void match_cfg_def() { Preprocessor preprocessor(nullptr, this); { std::map cfg; ASSERT_EQUALS(false, preprocessor.match_cfg_def(cfg, "A>1||defined(B)")); } { std::map cfg; cfg["A"] = ""; cfg["B"] = ""; ASSERT_EQUALS(true, preprocessor.match_cfg_def(cfg, "defined(A)&&defined(B)")); } { std::map cfg; cfg["ABC"] = ""; ASSERT_EQUALS(false, preprocessor.match_cfg_def(cfg, "defined(A)")); ASSERT_EQUALS(true, preprocessor.match_cfg_def(cfg, "!defined(A)")); ASSERT_EQUALS(false, preprocessor.match_cfg_def(cfg, "!defined(ABC)&&!defined(DEF)")); ASSERT_EQUALS(true, preprocessor.match_cfg_def(cfg, "!defined(A)&&!defined(B)")); } { std::map cfg; cfg["A"] = "1"; cfg["B"] = "2"; ASSERT_EQUALS(true, preprocessor.match_cfg_def(cfg, "A==1")); ASSERT_EQUALS(true, preprocessor.match_cfg_def(cfg, "A<2")); ASSERT_EQUALS(false, preprocessor.match_cfg_def(cfg, "A==2")); ASSERT_EQUALS(false, preprocessor.match_cfg_def(cfg, "A<1")); ASSERT_EQUALS(false, preprocessor.match_cfg_def(cfg, "A>=1&&B<=A")); ASSERT_EQUALS(true, preprocessor.match_cfg_def(cfg, "A==1 && A==1")); } } void if_cond1() { const char filedata[] = "#if LIBVER>100\n" " A\n" "#else\n" " B\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\n\nB\n\n", actual[""]); TODO_ASSERT_EQUALS("\nA\n\n\n\n", "", actual["LIBVER=101"]); } void if_cond2() { const char filedata[] = "#ifdef A\n" "a\n" "#endif\n" "#if defined(A) && defined(B)\n" "ab\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(3, static_cast(actual.size())); ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\na\n\n\n\n\n", actual["A"]); ASSERT_EQUALS("\na\n\n\nab\n\n", actual["A;B"]); 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"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(3, static_cast(actual.size())); ASSERT_EQUALS("\n!a\n\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\n\n\n\n\na\n\n", actual["A"]); ASSERT_EQUALS("\n!a\n\nb\n\n\n\n\n", actual["B"]); } 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"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(3, static_cast(actual.size())); ASSERT_EQUALS("\n!a\n\n\n\n!b\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\n\n\n\n\n\n\na\n\n", actual["A"]); ASSERT_EQUALS("\n!a\n\nb\n\n\n\n\n\n\n", actual["B"]); } 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"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(4, static_cast(actual.size())); ASSERT_EQUALS("\n!a\n\n\n\n!b\n\n\n\n\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\n\n\n\n\n\n\na\n\n\n\n!b\n\n\n", actual["A"]); ASSERT_EQUALS("\n\n\n\n\n\n\n\na\n\nb\n\n\n\n\n", actual["A;B"]); ASSERT_EQUALS("\n!a\n\nb\n\n\n\n\n\n\n\n\n\n\n\n", actual["B"]); } void if_cond2e() { const char filedata[] = "#if !defined(A)\n" "!a\n" "#elif !defined(B)\n" "!b\n" "#endif\n"; // Preprocess => actual result.. errout.str(""); std::istringstream istr(filedata); std::map actual; Settings settings; settings.debug = settings.debugwarnings = true; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(3, static_cast(actual.size())); ASSERT_EQUALS("\n!a\n\n\n\n", actual[""]); ASSERT_EQUALS("\n\n\n!b\n\n", actual["A"]); TODO_ASSERT_EQUALS("\n\n\n\n\n", "", actual["A;B"]); ASSERT_EQUALS("", errout.str()); } void if_cond3() { const char filedata[] = "#ifdef A\n" "a\n" "#if defined(B) && defined(C)\n" "abc\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(3, static_cast(actual.size())); ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\na\n\n\n\n\n", actual["A"]); ASSERT_EQUALS("\na\n\nabc\n\n\n", actual["A;B;C"]); } void if_cond4() { { const char filedata[] = "#define A\n" "#define B\n" "#if defined A || defined B\n" "ab\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\n\nab\n\n", actual[""]); } { const char filedata[] = "#if A\n" "{\n" "#if (defined(B))\n" "foo();\n" "#endif\n" "}\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(3, static_cast(actual.size())); ASSERT_EQUALS("\n\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\n{\n\n\n\n}\n\n", actual["A"]); ASSERT_EQUALS("\n{\n\nfoo();\n\n}\n\n", actual["A;B"]); } { const char filedata[] = "#define A\n" "#define B\n" "#if (defined A) || defined (B)\n" "ab\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\n\nab\n\n", actual[""]); } { const char filedata[] = "#if (A)\n" "foo();\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); ASSERT_EQUALS("\n\n\n", actual[""]); ASSERT_EQUALS("\nfoo();\n\n", actual["A"]); } { const char filedata[] = "#if! A\n" "foo();\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. TODO_ASSERT_EQUALS(2, 1, static_cast(actual.size())); ASSERT_EQUALS("\nfoo();\n\n", actual[""]); } } 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"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); ASSERT_EQUALS("\n\n\ncd\n\n\n\n", actual[""]); ASSERT_EQUALS("\nab\n\ncd\n\nef\n\n", actual["A;B"]); } void if_cond6() { const char filedata[] = "\n" "#if defined(A) && defined(B))\n" "#endif\n"; errout.str(""); // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("[file.c:2]: (error) mismatching number of '(' and ')' in this line: defined(A)&&defined(B))\n", errout.str()); } void if_cond8() { const char filedata[] = "#if defined(A) + defined(B) + defined(C) != 1\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n", actual[""]); } void if_cond9() { const char filedata[] = "#if !defined _A\n" "abc\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\nabc\n\n", actual[""]); } 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); } void if_cond11() { errout.str(""); 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); ASSERT_EQUALS("", errout.str()); } void if_cond12() { const char filedata[] = "#define A (1)\n" "#if A == 1\n" ";\n" "#endif\n"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n;\n\n", preprocessor.getcode(filedata,"","")); } void if_cond13() { const char filedata[] = "#if ('A' == 0x41)\n" "123\n" "#endif\n"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n123\n\n", preprocessor.getcode(filedata,"","")); } void if_cond14() { const char filedata[] = "#if !(A)\n" "123\n" "#endif\n"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n123\n\n", preprocessor.getcode(filedata,"","")); } void if_cond15() { // #4456 - segmentation fault const char filedata[] = "#if ((A >= B) && (C != D))\n" "#if (E < F(1))\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "4456.c"); // <- don't crash in Preprocessor::getcfgs -> Tokenize -> number of template parameters } void if_or_1() { const char filedata[] = "#if defined(DEF_10) || defined(DEF_11)\n" "a1;\n" "#endif\n"; errout.str(""); output.str(""); // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; settings.debug = settings.debugwarnings = true; settings.addEnabled("missingInclude"); Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n\n", actual[""]); // the "defined(DEF_10) || defined(DEF_11)" are not handled correctly.. ASSERT_EQUALS("(debug) unhandled configuration: defined(DEF_10)||defined(DEF_11)\n", errout.str()); TODO_ASSERT_EQUALS(2, 1, actual.size()); TODO_ASSERT_EQUALS("\na1;\n\n", "", actual["DEF_10"]); } void if_or_2() { const std::string code("#if X || Y\n" "a1;\n" "#endif\n"); Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\na1;\n\n", preprocessor.getcode(code, "X", "test.c")); ASSERT_EQUALS("\na1;\n\n", preprocessor.getcode(code, "Y", "test.c")); } void if_macro_eq_macro() { const std::string 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"); Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::map actual; preprocessor.preprocess(istr, actual, "file.c"); ASSERT_EQUALS("\n\n\n\nWilma\n\n\n\n", actual[""]); } void ticket_3675() { const std::string code("#ifdef YYSTACKSIZE\n" "#define YYMAXDEPTH YYSTACKSIZE\n" "#else\n" "#define YYSTACKSIZE YYMAXDEPTH\n" "#endif\n" "#if YYDEBUG\n" "#endif\n"); Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::map actual; preprocessor.preprocess(istr, actual, "file.c"); // There's nothing to assert. It just needs to not hang. } void ticket_3699() { const std::string code("#define INLINE __forceinline\n" "#define inline __forceinline\n" "#define __forceinline inline\n" "#if !defined(_WIN32)\n" "#endif\n" "INLINE inline __forceinline\n" ); Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::map actual; preprocessor.preprocess(istr, actual, "file.c"); // 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\n", actual[""]); } void ticket_4922() {// #4922 const std::string code("__asm__ \n" "{ int extern __value) 0; (double return (\"\" } extern\n" "__typeof __finite (__finite) __finite __inline \"__GI___finite\");"); Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::map actual; preprocessor.preprocess(istr, actual, "file.cpp"); } void multiline1() { const char filedata[] = "#define str \"abc\" \\\n" " \"def\" \n" "abcdef = str;\n"; // Preprocess => actual result.. std::istringstream istr(filedata); Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("#define str \"abc\" \"def\"\n\nabcdef = str;\n", preprocessor.read(istr, "test.c")); } void multiline2() { const char filedata[] = "#define sqr(aa) aa * \\\n" " aa\n" "sqr(5);\n"; // Preprocess => actual result.. std::istringstream istr(filedata); Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("#define sqr(aa) aa * aa\n\nsqr(5);\n", preprocessor.read(istr, "test.c")); } void multiline3() { const char filedata[] = "const char *str = \"abc\\\n" "def\\\n" "ghi\"\n"; // Preprocess => actual result.. std::istringstream istr(filedata); Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("const char *str = \"abcdefghi\"\n\n\n", preprocessor.read(istr, "test.c")); } void multiline4() { errout.str(""); const char filedata[] = "#define A int a = 4;\\ \n" " int b = 5;\n" "A\n"; // Preprocess => actual result.. Settings settings; std::istringstream istr(filedata); std::map actual; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); #ifdef __GNUC__ ASSERT_EQUALS("\n\n$int $a = $4; $int $b = $5;\n", actual[""]); #else ASSERT_EQUALS("\nint b = 5;\n$int $a = $4;\\\n", actual[""]); #endif ASSERT_EQUALS("", errout.str()); } void multiline5() { errout.str(""); const char filedata[] = "#define ABC int a /*\n" "*/= 4;\n" "int main(){\n" "ABC\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\nint main(){\n$int $a = $4;\n}\n", actual[""]); ASSERT_EQUALS("", errout.str()); } void remove_asm() { std::string str1("#asm\nmov ax,bx\n#endasm"); Preprocessor::removeAsm(str1); ASSERT_EQUALS("asm(\nmov ax,bx\n);", str1); std::string str2("\n#asm\nmov ax,bx\n#endasm\n"); Preprocessor::removeAsm(str2); ASSERT_EQUALS("\nasm(\nmov ax,bx\n);\n", str2); } void if_defined() { { const char filedata[] = "#if defined(AAA)\n" "#endif\n"; ASSERT_EQUALS("#ifdef AAA\n#endif\n", OurPreprocessor::replaceIfDefined(filedata)); } { ASSERT_EQUALS("#elif A\n", OurPreprocessor::replaceIfDefined("#elif defined(A)\n")); } } void if_not_defined() { const char filedata[] = "#if !defined(AAA)\n" "#endif\n"; ASSERT_EQUALS("#ifndef AAA\n#endif\n", OurPreprocessor::replaceIfDefined(filedata)); } void macro_simple1() { { const char filedata[] = "#define AAA(aa) f(aa)\n" "AAA(5);\n"; ASSERT_EQUALS("\n$f($5);\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define AAA(aa) f(aa)\n" "AAA (5);\n"; ASSERT_EQUALS("\n$f($5);\n", OurPreprocessor::expandMacros(filedata)); } } void macro_simple2() { const char filedata[] = "#define min(x,y) x $0 ) $return $1;\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple5() { 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{\n int temp = 0;\n $if( $temp > $0 ) $return $1;\n}\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple6() { const char filedata[] = "#define ABC (a+b+c)\n" "ABC\n"; ASSERT_EQUALS("\n$($a+$b+$c)\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple7() { const char filedata[] = "#define ABC(str) str\n" "ABC(\"(\")\n"; ASSERT_EQUALS("\n$\"(\"\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple8() { const char filedata[] = "#define ABC 123\n" "#define ABCD 1234\n" "ABC ABCD\n"; ASSERT_EQUALS("\n\n$123 $1234\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple9() { const char filedata[] = "#define ABC(a) f(a)\n" "ABC( \"\\\"\" );\n" "ABC( \"g\" );\n"; ASSERT_EQUALS("\n$f(\"\\\"\");\n$f(\"g\");\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple10() { const char filedata[] = "#define ABC(t) t x\n" "ABC(unsigned long);\n"; ASSERT_EQUALS("\n$unsigned $long $x;\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple11() { const char filedata[] = "#define ABC(x) delete x\n" "ABC(a);\n"; ASSERT_EQUALS("\n$delete $a;\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple12() { const char filedata[] = "#define AB ab.AB\n" "AB.CD\n"; ASSERT_EQUALS("\n$ab.$AB.CD\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple13() { const char filedata[] = "#define TRACE(x)\n" "TRACE(;if(a))\n"; ASSERT_EQUALS("\n$\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple14() { const char filedata[] = "#define A \" a \"\n" "printf(A);\n"; ASSERT_EQUALS("\nprintf($\" a \");\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple15() { const char filedata[] = "#define FOO\"foo\"\n" "FOO\n"; ASSERT_EQUALS("\n$\"foo\"\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple16() { // # 4703 const char filedata[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n" "MACRO( B\t, U , G )"; ASSERT_EQUALS("\n$class $BUGCreator{};", OurPreprocessor::expandMacros(filedata)); } void macro_simple17() { // # 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("\n$123+$123", OurPreprocessor::expandMacros(filedata)); } void macro_simple18() { // (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 char filedata[] = "#define A(m) long n = m; n++;\n" "#define B(n) A(n)\n" "B(0)\n"; ASSERT_EQUALS("\n\n$$long $n=$0;$n++;\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A B\n" "#define B 3\n" "A\n"; ASSERT_EQUALS("\n\n$$3\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "#define D(fmt, args...) DBG(fmt, ## args)\n" "DBG(\"hello\");\n"; ASSERT_EQUALS("\n\n$printf(\"hello\");\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "#define D(fmt, args...) DBG(fmt, ## args)\n" "DBG(\"hello: %d\",3);\n"; ASSERT_EQUALS("\n\n$printf(\"hello: %d\",$3);\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define BC(b, c...) 0##b * 0##c\n" "#define ABC(a, b...) a + BC(b)\n" "\n" "ABC(1);\n" "ABC(2,3);\n" "ABC(4,5,6);\n"; ASSERT_EQUALS("\n\n\n$1+$$0*$0;\n$2+$$03*$0;\n$4+$$05*$06;\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A 4\n" "#define B(a) a,A\n" "B(2);\n"; ASSERT_EQUALS("\n\n$2, $4;\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A(x) (x)\n" "#define B )A(\n" "#define C )A(\n"; ASSERT_EQUALS("\n\n\n", 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);\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define foo foo\n" "foo\n"; ASSERT_EQUALS("\n$foo\n", 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\n$void $foo0(){$do{$$}$while($0);}\n$void $foo1(){$do{$$}$while($0);}\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define B(x) (\n" "#define A() B(xx)\n" "B(1) A() ) )\n"; ASSERT_EQUALS("\n\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;\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define PTR1 (\n" "PTR1 PTR1\n"; ASSERT_EQUALS("\n$( $(\n", OurPreprocessor::expandMacros(filedata)); } } void macroInMacro2() { const char filedata[] = "#define A(x) a##x\n" "#define B 0\n" "A(B)\n"; ASSERT_EQUALS("\n\n$aB\n", OurPreprocessor::expandMacros(filedata)); } void macro_mismatch() { const char filedata[] = "#define AAA(aa,bb) f(aa)\n" "AAA(5);\n"; ASSERT_EQUALS("\nAAA(5);\n", OurPreprocessor::expandMacros(filedata)); } void macro_linenumbers() { const char filedata[] = "#define AAA(a)\n" "AAA(5\n" "\n" ")\n" "int a;\n"; ASSERT_EQUALS("\n$" "\n" "\n" "\n" "int a;\n", OurPreprocessor::expandMacros(filedata)); } void macro_nopar() { const char filedata[] = "#define AAA( ) { NULL }\n" "AAA()\n"; ASSERT_EQUALS("\n${ $NULL }\n", OurPreprocessor::expandMacros(filedata)); } void macro_switchCase() { { // 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("\n$switch($a){$case $2:$break;};\n", OurPreprocessor::expandMacros(filedata)); } { // Make sure "2 BB" doesn't become "2BB" const char filedata[] = "#define A() AA : 2 BB\n" "A();\n"; ASSERT_EQUALS("\n$AA : $2 $BB;\n", 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;;\n", 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;;\n", 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;;\n", OurPreprocessor::expandMacros(filedata)); } } void macro_NULL() { // Let the tokenizer handle NULL. // See ticket #4482 - UB when passing NULL to variadic function ASSERT_EQUALS("\n$0", OurPreprocessor::expandMacros("#define null 0\nnull")); ASSERT_EQUALS("\nNULL", OurPreprocessor::expandMacros("#define NULL 0\nNULL")); } void string1() { const char filedata[] = "int main()" "{" " const char *a = \"#define A\n\";" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("int main(){ const char *a = \"#define A\n\";}\n", actual[""]); } void string2() { const char filedata[] = "#define AAA 123\n" "str = \"AAA\"\n"; // Compare results.. ASSERT_EQUALS("\nstr = \"AAA\"\n", OurPreprocessor::expandMacros(filedata)); } void string3() { const char filedata[] = "str(\";\");\n"; // Compare results.. ASSERT_EQUALS("str(\";\");\n", 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\n$char $b=$0;\n", OurPreprocessor::expandMacros(filedata)); } { // ticket #403 const char filedata[] = "#define z p[2]\n" "#undef z\n" "int z;\n" "z = 0;\n"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\nint z;\nz = 0;\n", preprocessor.getcode(filedata, "", "")); } } void defdef() { const char filedata[] = "#define AAA 123\n" "#define AAA 456\n" "#define AAA 789\n" "AAA\n"; // Compare results.. ASSERT_EQUALS("\n\n\n$789\n", OurPreprocessor::expandMacros(filedata)); } void preprocessor_doublesharp() { // simple testcase without ## const char filedata1[] = "#define TEST(var,val) var = val\n" "TEST(foo,20);\n"; ASSERT_EQUALS("\n$foo=$20;\n", OurPreprocessor::expandMacros(filedata1)); // simple testcase with ## const char filedata2[] = "#define TEST(var,val) var##_##val = val\n" "TEST(foo,20);\n"; ASSERT_EQUALS("\n$foo_20=$20;\n", OurPreprocessor::expandMacros(filedata2)); // concat macroname const char filedata3[] = "#define ABCD 123\n" "#define A(B) A##B\n" "A(BCD)\n"; ASSERT_EQUALS("\n\n$$123\n", 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\n$$AAB\n", 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\n$$abc\n", 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\n$$$x_y\n", OurPreprocessor::expandMacros(filedata6)); } void preprocessor_include_in_str() { const char filedata[] = "int main()\n" "{\n" "const char *a = \"#include \n\";\n" "return 0;\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("int main()\n{\nconst char *a = \"#include \n\";\nreturn 0;\n}\n", actual[""]); } void va_args_1() { 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("\n$printf(\"[0x%lx-0x%lx)\",$pstart,$pend);\n", actual); } void va_args_2() { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "DBG(\"hello\");\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n$printf(\"hello\");\n", actual); } void va_args_3() { const char filedata[] = "#define FRED(...) { fred(__VA_ARGS__); }\n" "FRED(123)\n"; ASSERT_EQUALS("\n${ $fred($123); }\n", OurPreprocessor::expandMacros(filedata)); } void va_args_4() { const char filedata[] = "#define FRED(name, ...) name (__VA_ARGS__)\n" "FRED(abc, 123)\n"; ASSERT_EQUALS("\n$abc($123)\n", OurPreprocessor::expandMacros(filedata)); } 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\nint main()\n{\nif( $'ABCD' == 0 );\nreturn 0;\n}\n", actual[""]); } void stringify() { const char filedata[] = "#define STRINGIFY(x) #x\n" "STRINGIFY(abc)\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n$\"abc\"\n", actual); } void stringify2() { const char filedata[] = "#define A(x) g(#x)\n" "A(abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n$g(\"abc\");\n", actual); } void stringify3() { const char filedata[] = "#define A(x) g(#x)\n" "A( abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n$g(\"abc\");\n", actual); } void stringify4() { 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 $\n\n\"abc\" 2\n", actual); } void stringify5() { const char filedata[] = "#define A(x) a(#x,x)\n" "A(foo(\"\\\"\"))\n"; ASSERT_EQUALS("\n$a(\"foo(\\\"\\\\\\\"\\\")\",$foo(\"\\\"\"))\n", OurPreprocessor::expandMacros(filedata)); } void pragma() { const char filedata[] = "#pragma once\n" "void f()\n" "{\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\nvoid f()\n{\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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\n\naaa\n\n\n\nbbb\n", 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\nasm(temp);\nbbb\n", actual[""]); } void pragma_once() { const char code[] = "#pragma once\n" "int x"; Preprocessor preprocessor(nullptr, this); const std::list includePaths; std::map defs; std::set pragmaOnce; preprocessor.handleIncludes(code, "123.h", includePaths, defs, pragmaOnce, std::list()); ASSERT_EQUALS(1U, pragmaOnce.size()); ASSERT_EQUALS("123.h", *(pragmaOnce.begin())); } void endifsemicolon() { const char filedata[] = "void f() {\n" "#ifdef A\n" "#endif;\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); const std::string expected("void f() {\n\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"; const char expected[] = "\n" "\n" "\n" "void f()\n" "{\n" "char a = 'a';\n" "}\n"; errout.str(""); // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); ASSERT_EQUALS(expected, 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("\n\nint a = $1;\n", actual); ASSERT_EQUALS("", 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 unicodeInCode() { const std::string filedata("a\xC8"); std::istringstream istr(filedata); errout.str(""); Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.read(istr, "test.cpp"); ASSERT_EQUALS("[test.cpp:1]: (error) The code contains characters that are unhandled. Neither unicode nor extended ASCII are supported. (line=1, character code=c8)\n", errout.str()); } void unicodeInComment() { const std::string filedata("//\xC8"); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("", preprocessor.read(istr, "test.cpp")); } void unicodeInString() { const std::string filedata("\"\xC8\""); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS(filedata, preprocessor.read(istr, "test.cpp")); } void define_part_of_func() { errout.str(""); const char filedata[] = "#define A g(\n" "void f() {\n" " A );\n" " }\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\nvoid f() {\n$g( );\n}\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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(2, static_cast(actual.size())); ASSERT_EQUALS("\n\n\n\n\n$20\n", actual[""]); ASSERT_EQUALS("\n\n\n\n\n$10\n", actual["A"]); ASSERT_EQUALS("", errout.str()); } void multiline_comment() { errout.str(""); const char filedata[] = "#define ABC {// \\\n" "}\n" "void f() ABC }\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\n\nvoid f() ${ }\n", actual[""]); 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c", std::list()); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("[file.c:6]: (error) Syntax error. Not enough parameters for macro 'BC'.\n", errout.str()); } void newline_in_macro() { errout.str(""); const char filedata[] = "#define ABC(str) printf( str )\n" "void f()\n" "{\n" " ABC(\"\\n\");\n" "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c", std::list()); // Compare results.. ASSERT_EQUALS(1, static_cast(actual.size())); ASSERT_EQUALS("\nvoid f()\n{\n$printf(\"\\n\");\n}\n", actual[""]); ASSERT_EQUALS("", errout.str()); } void includes() { { std::string src = "#include a.h"; ASSERT_EQUALS(OurPreprocessor::NoHeader, OurPreprocessor::getHeaderFileName(src)); ASSERT_EQUALS("", src); } { std::string src = "#include \"b.h\""; ASSERT_EQUALS(OurPreprocessor::UserHeader, OurPreprocessor::getHeaderFileName(src)); ASSERT_EQUALS("b.h", src); } { std::string src = "#include "; ASSERT_EQUALS(OurPreprocessor::SystemHeader, OurPreprocessor::getHeaderFileName(src)); ASSERT_EQUALS("c.h", src); } { std::string src = "#include \"d/d.h\""; ASSERT_EQUALS(OurPreprocessor::UserHeader, OurPreprocessor::getHeaderFileName(src)); ASSERT_EQUALS("d/d.h", src); } { std::string src = "#include \"e\\e.h\""; ASSERT_EQUALS(OurPreprocessor::UserHeader, OurPreprocessor::getHeaderFileName(src)); ASSERT_EQUALS("e/e.h", src); } } 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n\n\n", actual[""]); ASSERT_EQUALS("\nA\n\n\nA\n\n", actual["ABC"]); ASSERT_EQUALS(2, static_cast(actual.size())); } void define_if1() { Preprocessor preprocessor(nullptr, this); { const char filedata[] = "#define A 0\n" "#if A\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\n\n", preprocessor.getcode(filedata,"","")); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO\n\n", preprocessor.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"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n\nFOO\n\n", preprocessor.getcode(filedata,"","")); } void define_if3() { const char filedata[] = "#define A 0\n" "#if (A==0)\n" "FOO\n" "#endif"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\nFOO\n\n", preprocessor.getcode(filedata,"","")); } void define_if4() { const char filedata[] = "#define X +123\n" "#if X==123\n" "FOO\n" "#endif"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\nFOO\n\n", preprocessor.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"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n\nFOO\n\n", preprocessor.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"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n\n\nFOO\n\n", preprocessor.getcode(filedata,"","")); } { const char filedata[] = "#define A (1+A)\n" // don't hang for recursive macros "#if A==1\n" "FOO\n" "#endif"; Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n\n\n", preprocessor.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"; Preprocessor preprocessor(nullptr, this); const std::string actualA0 = preprocessor.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 = preprocessor.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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\nB\n\n", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } { const char filedata[] = "#define A 1\n" "#ifdef A\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n$1\n\n", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n$1\n\n", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } { const char filedata[] = "#define A 1\n" "#if A>0\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n$1\n\n", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } { const char filedata[] = "#define A 1\n" "#if 0\n" "#undef A\n" "#endif\n" "A\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n$1\n", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } } void define_ifndef1() { const char filedata[] = "#define A(x) (x)\n" "#ifndef A\n" ";\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("\n\n\n\n", actual[""]); ASSERT_EQUALS(1U, actual.size()); } 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.. Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n\n\n\n\n$int me;\n", preprocessor.getcode(filedata, "", "a.cpp")); ASSERT_EQUALS("\n\n\n\n\n\n$char me;\n", preprocessor.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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n$123;\n", actual[""]); } void undef_ifdef() { const char filedata[] = "#undef A\n" "#ifdef A\n" "123\n" "#endif\n"; // Preprocess => actual result.. Preprocessor preprocessor(nullptr, this); ASSERT_EQUALS("\n\n\n\n", preprocessor.getcode(filedata, "", "a.cpp")); ASSERT_EQUALS("\n\n\n\n", preprocessor.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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("char a[] = \"#endfile\";\nchar b[] = \"#endfile\";\n\n", 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // B will always be defined if A is defined; the following test // cases should be fixed whenever this other bug is fixed TODO_ASSERT_EQUALS(2, 3, static_cast(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 testPreprocessorRead1() { const std::string filedata("/*\n*/ # /*\n*/ defi\\\nne FO\\\nO 10\\\n20"); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("#define FOO 1020", preprocessor.read(istr, "test.cpp")); } void testPreprocessorRead2() { const std::string filedata("\"foo\\\\\nbar\""); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("\"foo\\bar\"", preprocessor.read(istr, "test.cpp")); } void testPreprocessorRead3() { const std::string filedata("#define A \" a \"\n\" b\""); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS(filedata, preprocessor.read(istr, "test.cpp")); } void testPreprocessorRead4() { { // test < \\> < > (unescaped) const std::string filedata("#define A \" \\\\\"/*space*/ \" \""); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("#define A \" \\\\\" \" \"", preprocessor.read(istr, "test.cpp")); } { // test <" \\\" "> (unescaped) const std::string filedata("#define A \" \\\\\\\" \""); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("#define A \" \\\\\\\" \"", preprocessor.read(istr, "test.cpp")); } { // test <" \\\\"> <" "> (unescaped) const std::string filedata("#define A \" \\\\\\\\\"/*space*/ \" \""); std::istringstream istr(filedata); Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS("#define A \" \\\\\\\\\" \" \"", preprocessor.read(istr, "test.cpp")); } } void invalid_define_1() { Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream src("#define =\n"); std::string processedFile; std::list cfg; std::list paths; preprocessor.preprocess(src, processedFile, cfg, "", paths); // don't hang } void invalid_define_2() { // #4036 - hang Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream src("#define () {(int f(x) }\n"); std::string processedFile; std::list cfg; std::list paths; preprocessor.preprocess(src, processedFile, cfg, "", paths); // don't hang } void missingInclude() { Settings settings; Preprocessor preprocessor(&settings, this); Preprocessor::missingIncludeFlag = false; std::istringstream src("#include \"missing.h\"\n"); std::string processedFile; std::list cfg; std::list paths; ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag); preprocessor.preprocess(src, processedFile, cfg, "test.c", paths); ASSERT_EQUALS(true, Preprocessor::missingIncludeFlag); } 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"); Preprocessor preprocessor(nullptr, this); std::string actual = preprocessor.getcode(src, "X=1", "test.c"); ASSERT_EQUALS("\nFred & Wilma\n\n", actual); } void predefine2() { const std::string src("#if defined(X) && Y\n" "Fred & Wilma\n" "#endif\n"); { Preprocessor preprocessor(nullptr, this); std::string actual = preprocessor.getcode(src, "X=1", "test.c"); ASSERT_EQUALS("\n\n\n", actual); } { Preprocessor preprocessor(nullptr, this); std::string actual = preprocessor.getcode(src, "X=1;Y=2", "test.c"); ASSERT_EQUALS("\nFred & Wilma\n\n", 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"; Preprocessor preprocessor(nullptr,this); const std::string actual = preprocessor.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("\n\n\nFred & Wilma\n\n", actual); } void predefine4() { // #3577 const char code[] = "char buf[X];\n"; Preprocessor preprocessor(nullptr,this); const std::string actual = preprocessor.getcode(code, "X=123", "test.c"); ASSERT_EQUALS("char buf[$123];\n", actual); } void predefine5() { // #3737, #5119 - automatically define __cplusplus // #3737... const char code[] = "#ifdef __cplusplus\n123\n#endif"; Preprocessor preprocessor(nullptr,this); ASSERT_EQUALS("\n\n\n", preprocessor.getcode(code, "X=123", "test.c")); ASSERT_EQUALS("\n123\n\n", preprocessor.getcode(code, "X=123", "test.cpp")); // #5119... ASSERT_EQUALS(false, Preprocessor::cplusplus(nullptr,"test.c")); ASSERT_EQUALS(true, Preprocessor::cplusplus(nullptr,"test.cpp")); Settings settings; ASSERT_EQUALS(true, Preprocessor::cplusplus(&settings,"test.cpp")); settings.userUndefs.insert("__cplusplus"); ASSERT_EQUALS(false, Preprocessor::cplusplus(&settings,"test.cpp")); } void predefine6() { // #3737 - using -D and -f => check all matching configurations const char filedata[] = "#ifdef A\n" "1\n" "#else\n" "2\n" "#endif\n" "#ifdef B\n" "3\n" "#else\n" "4\n" "#endif"; // actual result.. Settings settings; Preprocessor preprocessor(&settings, this); std::map defs; defs["A"] = "1"; const std::list configs = preprocessor.getcfgs(filedata, "test1.c", defs); // Compare actual result with expected result.. ASSERT_EQUALS(2U, configs.size()); ASSERT_EQUALS("", configs.front()); ASSERT_EQUALS("B", configs.back()); } void simplifyCondition() { // Ticket #2794 std::map cfg; cfg["C"] = ""; std::string condition("defined(A) || defined(B) || defined(C)"); Preprocessor preprocessor(nullptr, this); preprocessor.simplifyCondition(cfg, condition, true); ASSERT_EQUALS("1", condition); } void invalidElIf() { // #2942 - segfault const char code[] = "#elif (){\n"; Preprocessor preprocessor(nullptr,this); const std::string actual = preprocessor.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("\n", actual); } void def_handleIncludes() { const std::string filePath("test.c"); const std::list includePaths; std::map defs; Preprocessor preprocessor(nullptr, this); // ifdef { defs.clear(); defs["A"] = ""; { std::set pragmaOnce; const std::string code("#ifdef A\n123\n#endif\n"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n123\n\n", actual); }{ std::set pragmaOnce; const std::string code("#ifdef B\n123\n#endif\n"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n", actual); } } // ifndef { defs.clear(); defs["A"] = ""; { std::set pragmaOnce; const std::string code("#ifndef A\n123\n#endif\n"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n", actual); }{ std::set pragmaOnce; const std::string code("#ifndef B\n123\n#endif\n"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n123\n\n", actual); } } // define - ifndef { std::set pragmaOnce; defs.clear(); const std::string code("#ifndef X\n#define X\n123\n#endif\n" "#ifndef X\n#define X\n123\n#endif\n"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n#define X\n123\n\n" "\n\n\n\n", actual); } // #define => #if { std::set pragmaOnce; defs.clear(); const std::string code("#define X 123\n" "#if X==123\n" "456\n" "#endif\n"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("#define X 123\n\n456\n\n", actual); } // #elif { const std::string code("#if defined(A)\n" "1\n" "#elif defined(B)\n" "2\n" "#elif defined(C)\n" "3\n" "#else\n" "4\n" "#endif"); { std::set pragmaOnce; defs.clear(); defs["A"] = ""; defs["C"] = ""; const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n1\n\n\n\n\n\n\n\n", actual); } { std::set pragmaOnce; defs.clear(); defs["B"] = ""; const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n2\n\n\n\n\n\n", actual); } { std::set pragmaOnce; defs.clear(); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n\n\n\n\n4\n\n", actual); } } // #endif { // see also endifsemicolon const std::string code("{\n#ifdef X\n#endif;\n}"); std::set pragmaOnce; defs.clear(); defs["Z"] = ""; const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("{\n\n\n}\n", actual); } // #undef { const std::string code("#ifndef X\n" "#define X\n" "123\n" "#endif\n"); std::set pragmaOnce; pragmaOnce.clear(); defs.clear(); const std::string actual1(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); pragmaOnce.clear(); defs.clear(); const std::string actual(preprocessor.handleIncludes(code + "#undef X\n" + code, filePath, includePaths, defs,pragmaOnce,std::list())); ASSERT_EQUALS(actual1 + "#undef X\n" + actual1, actual); } // #error { errout.str(""); std::set pragmaOnce; defs.clear(); const std::string code("#ifndef X\n#error abc\n#endif"); const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n#error abc\n\n", actual); } } void def_missingInclude() { const std::list includePaths; std::map defs; defs["AA"] = ""; Settings settings; Preprocessor preprocessor(&settings,this); // missing local include { const std::string code("#include \"missing-include!!.h\"\n"); std::set pragmaOnce; pragmaOnce.clear(); errout.str(""); settings = Settings(); preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); pragmaOnce.clear(); errout.str(""); settings.checkConfiguration = true; preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("[test.c:1]: (information) Include file: \"missing-include!!.h\" not found.\n", errout.str()); pragmaOnce.clear(); errout.str(""); settings.nomsg.addSuppression("missingInclude"); preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); } // missing system header { const std::string code("#include \n"); std::set pragmaOnce; pragmaOnce.clear(); errout.str(""); settings = Settings(); preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); pragmaOnce.clear(); errout.str(""); settings.checkConfiguration = true; preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("[test.c:1]: (information) Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.\n", errout.str()); pragmaOnce.clear(); errout.str(""); settings.nomsg.addSuppression("missingIncludeSystem"); preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); pragmaOnce.clear(); errout.str(""); settings = Settings(); settings.nomsg.addSuppression("missingInclude"); preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); } // #3285 - #elif { const std::string code("#ifdef GNU\n" "#elif defined(WIN32)\n" "#include \"missing-include!!.h\"\n" "#endif"); defs.clear(); defs["GNU"] = ""; std::set pragmaOnce; errout.str(""); settings = Settings(); preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); } } void def_handleIncludes_ifelse1() { const std::string filePath("test.c"); const std::list includePaths; std::map defs; Preprocessor preprocessor(nullptr, this); // #3405 { std::set pragmaOnce; defs.clear(); defs["A"] = ""; const std::string code("\n#ifndef PAL_UTIL_UTILS_H_\n" "#define PAL_UTIL_UTILS_H_\n" "1\n" "#ifndef USE_BOOST\n" "2\n" "#else\n" "3\n" "#endif\n" "4\n" "#endif\n" "\n" "#ifndef PAL_UTIL_UTILS_H_\n" "#define PAL_UTIL_UTILS_H_\n" "5\n" "#ifndef USE_BOOST\n" "6\n" "#else\n" "7\n" "#endif\n" "8\n" "#endif\n" "\n"); std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); // the 1,2,4 should be in the result actual.erase(0, actual.find("1")); while (actual.find("\n") != std::string::npos) actual.erase(actual.find("\n"),1); ASSERT_EQUALS("124", actual); } // #3418 { std::set pragmaOnce; defs.clear(); const char code[] = "#define A 1\n" "#define B A\n" "#if A == B\n" "123\n" "#endif\n"; std::string actual(preprocessor.handleIncludes(code, filePath, includePaths, defs,pragmaOnce,std::list())); ASSERT_EQUALS("#define A 1\n#define B A\n\n123\n\n", actual); } } void def_handleIncludes_ifelse2() { // #3651 const char code[] = "#if defined(A)\n" "\n" "#if defined(B)\n" "#endif\n" "\n" "#elif defined(C)\n" "\n" "#else\n" "\n" "123\n" "\n" "#endif"; Preprocessor preprocessor(nullptr, this); const std::list includePaths; std::map defs; defs["A"] = "1"; std::set pragmaOnce; ASSERT_EQUALS(std::string::npos, // No "123" in the output preprocessor.handleIncludes(code, "test.c", includePaths, defs, pragmaOnce,std::list()).find("123")); } void def_handleIncludes_ifelse3() { // #4865 const char code[] = "#ifdef A\n" "#if defined(SOMETHING_NOT_DEFINED)\n" "#else\n" "#endif\n" "#else\n" "#endif"; Settings settings; settings.userUndefs.insert("A"); Preprocessor preprocessor(&settings, this); const std::list includePaths; std::map defs; defs["B"] = "1"; defs["C"] = "1"; std::set pragmaOnce; preprocessor.handleIncludes(code, "test.c", includePaths, defs, pragmaOnce, std::list()); // don't crash } void def_valueWithParentheses() { // #define should introduce a new symbol regardless of parentheses in the value // and regardless of white space in weird places (people do this for some reason). const char code[] = "#define A (Fred)\n" " # define B (Flintstone)\n" " #define C (Barney)\n" "\t#\tdefine\tD\t(Rubble)\t\t\t\n"; const std::string filePath("test.c"); const std::list includePaths; std::map defs; std::set pragmaOnce; Preprocessor preprocessor(nullptr, this); std::istringstream istr(code); const std::string s(preprocessor.read(istr, "")); preprocessor.handleIncludes(s, filePath, includePaths, defs, pragmaOnce, std::list()); ASSERT(defs.find("A") != defs.end()); ASSERT_EQUALS("(Fred)", defs["A"]); ASSERT(defs.find("B") != defs.end()); ASSERT_EQUALS("(Flintstone)", defs["B"]); ASSERT(defs.find("C") != defs.end()); ASSERT_EQUALS("(Barney)", defs["C"]); ASSERT(defs.find("D") != defs.end()); ASSERT_EQUALS("(Rubble)", defs["D"]); } void undef1() { Settings settings; const char filedata[] = "#ifdef X\n" "Fred & Wilma\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n", actual[""]); } void undef2() { Settings settings; const char filedata[] = "#ifndef X\n" "Fred & Wilma\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\nFred & Wilma\n\n", actual[""]); } void undef3() { Settings settings; const char filedata[] = "#define X\n" "#ifdef X\n" "Fred & Wilma\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n\n", actual[""]); } void undef4() { Settings settings; const char filedata[] = "#define X Y\n" "#ifdef X\n" "Fred & Wilma\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n\n", actual[""]); } void undef5() { Settings settings; const char filedata[] = "#define X() Y\n" "#ifdef X\n" "Fred & Wilma\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n\n", actual[""]); } void undef6() { Settings settings; const char filedata[] = "#define X Y\n" "#ifdef X\n" "Fred & Wilma\n" "#else\n" "Barney & Betty\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n\nBarney & Betty\n\n", actual[""]); } void undef7() { Settings settings; const char filedata[] = "#define X XDefined\n" "X;\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); TODO_ASSERT_EQUALS("\n;\n","\n$XDefined;\n", actual[""]); } void undef8() { Settings settings; const char filedata[] = "#ifdef HAVE_CONFIG_H\n" "#include \"config.h\"\n" "#endif\n" "\n" "void foo();\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines settings.checkConfiguration = true; errout.str(""); Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS("[file.c:2]: (information) Include file: \"config.h\" not found.\n", errout.str()); ASSERT_EQUALS("\n\n\n\n\n", actual[""]); } void undef9() { Settings settings; const char filedata[] = "#define X Y\n" "#ifndef X\n" "Fred & Wilma\n" "#else\n" "Barney & Betty\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); std::map actual; settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\nFred & Wilma\n\n\n\n", actual[""]); } void undef10() { Settings settings; const char filedata[] = "#ifndef X\n" "#endif\n" "#ifndef Y\n" "#endif\n"; // Preprocess => actual result.. std::istringstream istr(filedata); settings.userUndefs.insert("X"); // User undefs should override internal defines Preprocessor preprocessor(&settings, this); std::string processedFile; std::list resultConfigurations; const std::list includePaths; preprocessor.preprocess(istr, processedFile, resultConfigurations, "file.c", includePaths); // Compare results. Two configurations "" and "Y". No "X". ASSERT_EQUALS(2U, resultConfigurations.size()); ASSERT_EQUALS("", resultConfigurations.front()); ASSERT_EQUALS("Y", resultConfigurations.back()); } void handleUndef() { Settings settings; settings.userUndefs.insert("X"); const Preprocessor preprocessor(&settings, this); std::list configurations; // configurations to keep configurations.clear(); configurations.push_back("XY;"); configurations.push_back("AX;"); configurations.push_back("A;XY"); preprocessor.handleUndef(configurations); ASSERT_EQUALS(3U, configurations.size()); // configurations to remove configurations.clear(); configurations.push_back("X;Y"); configurations.push_back("X=1;Y"); configurations.push_back("A;X;B"); configurations.push_back("A;X=1;B"); configurations.push_back("A;X"); configurations.push_back("A;X=1"); preprocessor.handleUndef(configurations); ASSERT_EQUALS(0U, configurations.size()); } void macroChar() { const char filedata[] = "#define X 1\nX\n"; ASSERT_EQUALS("\n$1\n", OurPreprocessor::expandMacros(filedata,nullptr)); OurPreprocessor::macroChar = char(1); ASSERT_EQUALS("\n" + std::string(char(1),1U) + "1\n", OurPreprocessor::expandMacros(filedata,nullptr)); OurPreprocessor::macroChar = '$'; } void validateCfg() { Settings settings; Preprocessor preprocessor(&settings, this); ASSERT_EQUALS(true, preprocessor.validateCfg("", "X=42")); // don't hang when parsing cfg ASSERT_EQUALS(false, preprocessor.validateCfg("int y=Y;", "X=42;Y")); ASSERT_EQUALS(false, preprocessor.validateCfg("int x=X;", "X")); ASSERT_EQUALS(false, preprocessor.validateCfg("X=1;", "X")); ASSERT_EQUALS(true, preprocessor.validateCfg("int x=X;", "Y")); ASSERT_EQUALS(true, preprocessor.validateCfg("FOO_DEBUG()", "DEBUG")); ASSERT_EQUALS(true, preprocessor.validateCfg("\"DEBUG()\"", "DEBUG")); ASSERT_EQUALS(true, preprocessor.validateCfg("\"\\\"DEBUG()\"", "DEBUG")); ASSERT_EQUALS(false, preprocessor.validateCfg("\"DEBUG()\" DEBUG", "DEBUG")); ASSERT_EQUALS(true, preprocessor.validateCfg("#undef DEBUG", "DEBUG")); // #4301: // #ifdef A // int a = A; // <- using macro. must use -D so "A" will get a proper value errout.str(""); settings.addEnabled("all"); preprocessor.setFile0("test.c"); ASSERT_EQUALS(false, preprocessor.validateCfg("int a=A;", "A")); ASSERT_EQUALS("[test.c:1]: (information) Skipping configuration 'A' since the value of 'A' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str()); // #4949: // #ifdef A // a |= A; // <- using macro. must use -D so "A" will get a proper value errout.str(""); Settings settings1; settings = settings1; ASSERT_EQUALS("", preprocessor.getcode("if (x) a|=A;", "A", "test.c")); ASSERT_EQUALS("", errout.str()); settings.addEnabled("information"); ASSERT_EQUALS("", preprocessor.getcode("if (x) a|=A;", "A", "test.c")); ASSERT_EQUALS("[test.c:1]: (information) Skipping configuration 'A' since the value of 'A' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str()); } void if_sizeof() { // #4071 static const char* code = "#if sizeof(unsigned short) == 2\n" "Fred & Wilma\n" "#elif sizeof(unsigned short) == 4\n" "Fred & Wilma\n" "#else\n" "#endif"; Settings settings; Preprocessor preprocessor(&settings, this); std::istringstream istr(code); std::map actual; preprocessor.preprocess(istr, actual, "file.c"); ASSERT_EQUALS("\nFred & Wilma\n\n\n\n\n", actual[""]); } void double_include() { const char code[] = "int x"; Preprocessor preprocessor(nullptr, this); std::list includePaths; includePaths.push_back("."); includePaths.push_back("."); std::map defs; std::set pragmaOnce; preprocessor.handleIncludes(code, "123.h", includePaths, defs, pragmaOnce, std::list()); } 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::istringstream istr(filedata); std::map actual; Settings settings; Preprocessor preprocessor(&settings, this); preprocessor.preprocess(istr, actual, "file.c"); } }; REGISTER_TEST(TestPreprocessor) cppcheck-1.66/test/testrunner.cpp000066400000000000000000000020141236713773000171300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "options.h" int main(int argc, char *argv[]) { options args(argc, const_cast(argv)); std::size_t ret = TestFixture::runTests(args); return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } cppcheck-1.66/test/testrunner.vcxproj000066400000000000000000000356161236713773000200570ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {c183db5b-ad6c-423d-80ca-1f9549555a1a} {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4} testrunner Application Unicode false v120_xp Application Unicode false v120 Application Unicode false v120_xp Application Unicode false v120 ..\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\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true ..\cli;..\lib;..\externals;..\externals\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true ..\cli;..\lib;..\externals;..\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 true 4251;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true ..\cli;..\lib;..\externals;..\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;4512 true shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true false true Console true true true true cppcheck-1.66/test/testrunner.vcxproj.filters000066400000000000000000000164461236713773000215260ustar00rootroot00000000000000 {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 Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files cppcheck-1.66/test/testsamples.cpp000066400000000000000000000057471236713773000173030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "cppcheckexecutor.h" #include #include #include class TestSamples : public TestFixture { public: TestSamples() : TestFixture("TestSamples") { } private: void run() { TEST_CASE(runSamples); } void runSamples() const { REDIRECT; std::map files; #ifdef _WIN32 FileLister::recursiveAddFiles(files, "..\\samples"); #else FileLister::recursiveAddFiles(files, "samples"); #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; } } }; REGISTER_TEST(TestSamples) cppcheck-1.66/test/testsimplifytokens.cpp000066400000000000000000011613541236713773000207150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "token.h" #include "settings.h" #include "templatesimplifier.h" #include "path.h" #include #include extern std::ostringstream errout; class TestSimplifyTokens : public TestFixture { public: TestSimplifyTokens() : TestFixture("TestSimplifyTokens") { } private: void run() { // 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); 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 .. "( %var% )" TEST_CASE(declareVar); TEST_CASE(declareArray); TEST_CASE(dontRemoveIncrement); TEST_CASE(removePostIncrement); TEST_CASE(removePreIncrement); TEST_CASE(elseif1); TEST_CASE(ifa_ifa); // "if (a) { if (a) .." => "if (a) { if (1) .." TEST_CASE(sizeof2); TEST_CASE(sizeof3); TEST_CASE(sizeof4); 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); // #2599 TEST_CASE(sizeof23); // #2604 TEST_CASE(sizeofsizeof); TEST_CASE(casting); TEST_CASE(strlen1); TEST_CASE(strlen2); 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); 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(template29); // #3449 - don't crash for garbage code 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(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 TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateParameters1); // #4169 - segmentation fault TEST_CASE(namespaces); // Assignment in condition.. TEST_CASE(ifassign1); TEST_CASE(ifAssignWithCast); TEST_CASE(whileAssign1); TEST_CASE(whileAssign2); TEST_CASE(whileAssign3); // varid TEST_CASE(doWhileAssign); // varid TEST_CASE(test_4881); // similar to doWhileAssign (#4911), taken from #4881 with full code // "if(0==x)" => "if(!x)" TEST_CASE(ifnot); TEST_CASE(combine_wstrings); // 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(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(simplifyTypedef41); // ticket #1488 TEST_CASE(simplifyTypedef42); // ticket #1506 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(simplifyTypedef53); // ticket #1801 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(simplifyTypedef98); // ticket #2963 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(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(simplifyTypedefShadow); // #4445 - shadow variable TEST_CASE(simplifyOperator1); TEST_CASE(reverseArraySyntax) TEST_CASE(simplify_numeric_condition) TEST_CASE(simplify_condition); TEST_CASE(pointeralias1); TEST_CASE(pointeralias2); TEST_CASE(pointeralias3); TEST_CASE(pointeralias4); TEST_CASE(pointeralias5); TEST_CASE(reduceConstness); // simplify "while (0)" TEST_CASE(while0); // ticket #3140 TEST_CASE(while0for); TEST_CASE(while1); TEST_CASE(enum1); TEST_CASE(enum2); TEST_CASE(enum3); TEST_CASE(enum4); TEST_CASE(enum5); TEST_CASE(enum6); TEST_CASE(enum7); TEST_CASE(enum8); TEST_CASE(enum9); // ticket 1404 TEST_CASE(enum10); // ticket 1445 TEST_CASE(enum11); TEST_CASE(enum12); TEST_CASE(enum13); TEST_CASE(enum14); TEST_CASE(enum15); TEST_CASE(enum16); // ticket #1988 TEST_CASE(enum17); // ticket #2381 (duplicate enums) TEST_CASE(enum18); // #2466 (array with same name as enum constant) TEST_CASE(enum19); // ticket #2536 TEST_CASE(enum20); // ticket #2600 TEST_CASE(enum21); // ticket #2720 TEST_CASE(enum22); // ticket #2745 TEST_CASE(enum23); // ticket #2804 TEST_CASE(enum24); // ticket #2828 TEST_CASE(enum25); // ticket #2966 TEST_CASE(enum26); // ticket #2975 (segmentation fault) TEST_CASE(enum27); // ticket #3005 (segmentation fault) TEST_CASE(enum28); TEST_CASE(enum29); // ticket #3747 (bitwise or value) TEST_CASE(enum30); // ticket #3852 (false positive) TEST_CASE(enum31); // ticket #3934 (calculation in first item) TEST_CASE(enum32); // ticket #3998 (access violation) TEST_CASE(enum33); // ticket #4015 (segmentation fault) TEST_CASE(enum34); // ticket #4141 (division by zero) TEST_CASE(enum35); // ticket #3953 (avoid simplification of type) TEST_CASE(enum36); // ticket #4378 TEST_CASE(enum37); // ticket #4280 (shadow variable) TEST_CASE(enum38); // ticket #4463 (when throwing enum id, don't warn about shadow variable) TEST_CASE(enum39); // ticket #5145 (fp variable hides enum) TEST_CASE(enum40); TEST_CASE(enum41); // ticket #5212 (valgrind errors during enum simplification) TEST_CASE(enum42); // ticket #5182 (template function call in enum value) TEST_CASE(enum43); // lhs in assignment TEST_CASE(enumscope1); // ticket #3949 TEST_CASE(duplicateDefinition); // ticket #3565 TEST_CASE(invalid_enum); // #5600 // 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 = { .a = 3 }; => struct ABC abc; abc.a = 3; TEST_CASE(initstruct); // struct ABC { } abc; => struct ABC { }; ABC abc; TEST_CASE(simplifyStructDecl1); TEST_CASE(simplifyStructDecl2); // ticket #2579 TEST_CASE(simplifyStructDecl3); TEST_CASE(simplifyStructDecl4); TEST_CASE(simplifyStructDecl5); // ticket #3533 (segmentation fault) TEST_CASE(simplifyStructDecl6); // ticket #3732 TEST_CASE(simplifyStructDecl7); // ticket #476 (static anonymous struct array) // 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(removeUnnecessaryQualification1); TEST_CASE(removeUnnecessaryQualification2); TEST_CASE(removeUnnecessaryQualification3); TEST_CASE(removeUnnecessaryQualification4); TEST_CASE(removeUnnecessaryQualification5); TEST_CASE(removeUnnecessaryQualification6); // ticket #2859 TEST_CASE(removeUnnecessaryQualification7); // ticket #2970 TEST_CASE(removeUnnecessaryQualification8); TEST_CASE(removeUnnecessaryQualification9); // ticket #3151 TEST_CASE(removeUnnecessaryQualification10); // ticket #3310 segmentation fault TEST_CASE(simplifyIfNotNull); TEST_CASE(simplifyVarDecl1); // ticket # 2682 segmentation fault TEST_CASE(simplifyVarDecl2); // ticket # 2834 segmentation fault 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::Unspecified) { errout.str(""); Settings settings; settings.addEnabled("portability"); 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 settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(0, false); } std::string tokWithStdLib(const char code[]) { errout.str(""); Settings settings; if ((settings.library.load("./testrunner", "../cfg/std.cfg").errorcode != Library::OK) && (settings.library.load("./testrunner", "cfg/std.cfg").errorcode != Library::OK)) { complainMissingLib("std.cfg"); return ""; } Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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 cast() { ASSERT_EQUALS("if ( ! p ) { ; }", 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 ) { ; }", tok("if (p == (char *)(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)")); // dont 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 code1[] = "void foo()\n" "{\n" "const wchar_t *a =\n" "{\n" "L\"hello \"\n" "L\"world\"\n" "};\n" "}\n"; const char code2[] = "void foo()\n" "{\n" "const wchar_t *a =\n" "{\n" "\"hello world\"\n" "};\n" "}\n"; Settings settings; settings.platform(Settings::Unspecified); Tokenizer tokenizer(&settings, this); std::istringstream istr(code1); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS(tok(code2), tokenizer.tokens()->stringifyList(0, false)); ASSERT_EQUALS(true, tokenizer.tokens()->tokAt(13) && tokenizer.tokens()->tokAt(13)->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 ) { } }", tok("void f(){int *p; if (*(p) == 0) {}}")); ASSERT_EQUALS("void f ( ) { int * p ; if ( ! * p ) { } }", 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 ) { } }", 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;}")); // 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)); } 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)); } } std::string elseif(const char code[]) { errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.elseif(); return tokenizer.tokens()->stringifyList(false); } 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", elseif(code)); // syntax error: assert there is no segmentation fault ASSERT_EQUALS("\n\n##file 0\n1: else if ( x ) { }\n", elseif("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 ) { coo ( ) ; } " "else { " "if ( f == 1 ) " "{ " "goo ( ) ; " "} " "} " "} " "} " "}"; ASSERT_EQUALS(tok(expected), tok(src)); } } void ifa_ifa() { ASSERT_EQUALS("int a ; if ( a ) { { ab } cd }", tok("int a ; if (a) { if (a) { ab } cd }", true)); ASSERT_EQUALS("int a ; if ( a ) { { ab } cd }", tok("int a ; if (unlikely(a)) { if (a) { ab } cd }", true)); } unsigned int sizeofFromTokenizer(const char type[]) { errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(""); tokenizer.tokenize(istr, "test.cpp"); Token tok1(0); tok1.str(type); return tokenizer.sizeOfType(&tok1); } void sizeof2() { const char 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)); } void sizeof3() { const char 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)); } void sizeof4() { { const char code[] = "int i[10];\n" "sizeof(i[0]);\n"; ASSERT_EQUALS("int i [ 10 ] ; 4 ;", tok(code)); } { const char code[] = "int i[10];\n" "sizeof i[0];\n"; ASSERT_EQUALS("int i [ 10 ] ; 4 ;", 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::Unspecified)); ASSERT_EQUALS("; int i [ 10 ] ; 4 ;", tok(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() { // ticket #2599 segmentation fault const char code[] = "sizeof\n"; // don't segfault tok(code); } void sizeof23() { // ticket #2604 segmentation fault const char code[] = "sizeof <= A\n"; // don't segfault tok(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 std::string 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 std::string 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 std::string 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 template1() { const char code[] = "template void f(T val) { T a; }\n" "f(10);"; const std::string 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 std::string 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 std::string 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 std::string 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 std::string expected("template < classname 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 std::string 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 std::string 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 std::string wanted("template < typename T > class ABC { public: } ; " "int main ( ) { " "std :: vector < int > v ; " "v . push_back ( 4 ) ; " "return 0 ; " "}"); const std::string 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 std::string 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 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.. std::string expected("void f ( ) { A a ; } " "template < typename T > class B { void g ( ) { A b ; b = A :: h ( ) ; } } ; " "class A { } ; class A { } ;"); ASSERT_EQUALS(expected, tok(code)); } void template10() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " foo<3,int>();\n" "}\n"; // The expected result.. const std::string 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 std::string 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 std::string 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 std::string expected("void foo ( ) " "{ x ( ) ; } " "int main ( ) " "{ foo ( ) ; }"); ASSERT_EQUALS(expected, tok(code)); } void template15() { 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 std::string 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)); } 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 std::string expected("int main ( ) { b<2> ( ) ; return 0 ; } " "void b<2> ( ) { a<2> ( ) ; } " "void a ( ) { } " "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 std::string 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 std::string 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\n" "{\n" "public:\n" " ~A();\n" "};\n" "\n" "template A::~A()\n" "{\n" "}\n" "\n" "A a;\n"; // The expected result.. const std::string 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 std::string 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 std::string expected("Fred fred ; " "struct Fred { float data [ 4 ] ; } ;"); ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { Fred(); };\n" "Fred fred;"; const std::string expected("Fred fred ; " "struct Fred { Fred ( ) ; } ;"); ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const std::string 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 std::string 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 std::string 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_EQUALS("X ( template < class T > class Fred ) ;", tok(code)); } 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 template29() { // #3449 - garbage code (don't segfault) const char code[] = "template struct A;\n" "struct B { template struct C };\n" "{};"; ASSERT_EQUALS("template < typename T > struct A ; struct B { template < typename T > struct C } ; { } ;", 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)); } 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>> { } ; " // <- 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)); } 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" "}"; ASSERT_THROW(tok(code), InternalError); } 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 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 std::string 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 std::string 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 std::string 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 std::string 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[] = "template> class B {};\n" "template> class C {};\n" "template class D { };\n"; ASSERT_EQUALS("template < class T , class T2 > class B { } ; " "template < class B , typename C > class C { } ; " "template < class B , typename C > class D { } ;", 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 std::string 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)); } unsigned int templateParameters(const char code[]) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", "", true); return TemplateSimplifier::templateParameters(tokenizer.tokens()); } void templateParameters() { // Test that the function TemplateSimplifier::templateParameters works ASSERT_EQUALS(1U, templateParameters(" x;")); ASSERT_EQUALS(1U, templateParameters(" x;")); ASSERT_EQUALS(1U, templateParameters(" x;")); ASSERT_EQUALS(1U, templateParameters(" x;")); ASSERT_EQUALS(1U, templateParameters(" x;")); } void templateParameters1() { // #4169 - segmentation fault (invalid code) const char code[] = "volatile true , test < test < #ifdef __ppc__ true ,"; // do not crash on invalid code ASSERT_EQUALS(0, templateParameters(code)); } 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 std::string expected("namespace a { namespace b { void f ( ) { } } }"); ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "namespace b{ void f(){} }"; const std::string expected("namespace b { void f ( ) { } }"); ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f(int namespace) { }"; const std::string expected("void f ( int namespace ) { }"); ASSERT_EQUALS(expected, tok(code)); } } std::string simplifyIfAndWhileAssign(const char code[]) { errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, 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 ) " "{ " "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 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 ) ;", simplifyIfAndWhileAssign(";do { } while((current=f()) != NULL);")); } void ifnot() { ASSERT_EQUALS("if ( ! x ) { ; }", tok("if(0==x);", false)); ASSERT_EQUALS("if ( ! x ) { ; }", tok("if(x==0);", false)); ASSERT_EQUALS("if ( ! ( a = b ) ) { ; }", tok("if(0==(a=b));", false)); ASSERT_EQUALS("if ( ! a && b ( ) ) { ; }", tok("if( 0 == a && b() );", false)); ASSERT_EQUALS("if ( b ( ) && ! a ) { ; }", tok("if( b() && 0 == a );", false)); ASSERT_EQUALS("if ( ! ( a = b ) ) { ; }", tok("if((a=b)==0);", false)); ASSERT_EQUALS("if ( ! x . y ) { ; }", tok("if(x.y==0);", false)); ASSERT_EQUALS("if ( ! x ) { ; }", tok("if((x==0));", false)); ASSERT_EQUALS("if ( ( ! x ) && ! y ) { ; }", tok("if((x==0) && y==0);", false)); ASSERT_EQUALS("if ( ! ( ! fclose ( fd ) ) ) { ; }", tok("if(!(fclose(fd) == 0));", false)); } void not1() { ASSERT_EQUALS("void f ( ) { if ( ! p ) { ; } }", tok("void f() { if (not p); }", false)); ASSERT_EQUALS("void f ( ) { if ( p && ! q ) { ; } }", tok("void f() { if (p && not q); }", false)); ASSERT_EQUALS("void f ( ) { a = ! ( p && q ) ; }", tok("void f() { a = not(p && q); }", 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; };", false)); ASSERT_EQUALS("void f ( ) { not p ; compl c ; }", tok(" void f() { not p; compl c; }", false)); ASSERT_EQUALS("void foo ( not i ) ;", tok("void foo(not i);", false)); ASSERT_EQUALS("int foo ( not i ) { return g ( i ) ; }", tok("int foo(not i) { return g(i); }", false)); } void and1() { ASSERT_EQUALS("void f ( ) { if ( p && q ) { ; } }", tok("void f() { if (p and q) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) && q ) { ; } }", tok("void f() { if (foo() and q) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) && bar ( ) ) { ; } }", tok("void f() { if (foo() and bar()) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( p && bar ( ) ) { ; } }", tok("void f() { if (p and bar()) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( p && ! q ) { ; } }", tok("void f() { if (p and not q) ; }", false)); ASSERT_EQUALS("void f ( ) { r = a && b ; }", tok("void f() { r = a and b; }", false)); ASSERT_EQUALS("void f ( ) { r = ( a || b ) && ( c || d ) ; }", tok("void f() { r = (a || b) and (c || d); }", false)); } void or1() { ASSERT_EQUALS("void f ( ) { if ( p || q ) { ; } }", tok("void f() { if (p or q) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) || q ) { ; } }", tok("void f() { if (foo() or q) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( foo ( ) || bar ( ) ) { ; } }", tok("void f() { if (foo() or bar()) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( p || bar ( ) ) { ; } }", tok("void f() { if (p or bar()) ; }", false)); ASSERT_EQUALS("void f ( ) { if ( p || ! q ) { ; } }", tok("void f() { if (p or not q) ; }", false)); ASSERT_EQUALS("void f ( ) { r = a || b ; }", tok("void f() { r = a or b; }", false)); ASSERT_EQUALS("void f ( ) { r = ( a && b ) || ( c && d ) ; }", tok("void f() { r = (a && b) or (c && d); }", false)); } void cAlternativeTokens() { ASSERT_EQUALS("void f ( ) { err |= ( ( r & s ) && ! t ) ; }", tok("void f() { err or_eq ((r bitand s) and not t); }", 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) ; }", 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 ; delete b ; }", 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 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)); } { const char code[] = "a ? b = c , d : e ;"; // do nothing ASSERT_EQUALS(code, tok(code)); } { const char code[] = "; return a ? b = c , d : e ;"; // do nothing ASSERT_EQUALS(code, tok(code)); } { 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)); } } 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; }}"; ASSERT_EQUALS("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 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();\n" " y();\n" "}"; ASSERT_EQUALS("void f ( ) { exit ( ) ; }", 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); }")); } 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\");")); } std::string simplifyTypedef(const char code[]) { errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.list.createTokens(istr); tokenizer.createLinks(); tokenizer.simplifyTypedef(); return tokenizer.tokens()->stringifyList(0, false); } 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" "}\n"; const std::string 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" "};\n"; const std::string 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" "}\n"; const std::string 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" "}\n"; const std::string 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" "}\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" "}\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[] = "int e1 ; " "int 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); 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;\n"; // Clear the error buffer.. errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); tokenizer.validate(); } 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 > *);"; // Clear the error buffer.. errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); tokenizer.validate(); } 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" "};\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" "};\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" "};\n"; // static const gets changed to const static const char expected[] = "class Fred { " "" "const unsigned int * * get ( ) { return test ; } " "const static 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" "};\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 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)); } 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 ) ( const X & ) = new X ( * ) ( const X & ) [ 2 ] ; " "}"; ASSERT_EQUALS(expected, tok(code, false)); } // Check simplifyTypedef void checkSimplifyTypedef(const char code[]) { errout.str(""); // Tokenize.. Settings settings; settings.inconclusive = true; settings.addEnabled("style"); settings.debugwarnings = true; // show warnings about unhandled typedef Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); } 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)); checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (style, inconclusive) The typedef 'A' hides a typedef with the same name.\n" "[test.cpp:20] -> [test.cpp:1]: (style, inconclusive) The function parameter 'A' hides a typedef with the same name.\n" "[test.cpp:21] -> [test.cpp:1]: (style, inconclusive) The variable 'A' hides a typedef with the same name.\n" "[test.cpp:24] -> [test.cpp:1]: (style, inconclusive) The typedef 'A' hides a typedef with the same name.\n" "[test.cpp:24]: (debug) ValueFlow bailout: parameter a, at '{'\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() { { // ticket #1449 const char code[] = "template class V {};\n" "typedef V A;\n" "typedef int B;\n" "typedef V A;\n" "typedef int B;"; checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) The typedef 'A' hides a typedef with the same name.\n" "[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) The typedef 'B' hides a typedef with the same name.\n", errout.str()); } { 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)); checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef39() { const char code[] = "typedef int A;\n" "template ::value;"; const char expected[] = "template < const int , int > :: value ;"; ASSERT_EQUALS(expected, tok(code, false)); checkSimplifyTypedef(code); 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)); checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (style, inconclusive) The template parameter 'A' hides a typedef with the same name.\n" "[test.cpp:3] -> [test.cpp:2]: (style, inconclusive) The template parameter 'B' hides a typedef with the same name.\n", errout.str()); checkSimplifyTypedef("typedef tuple t2;\n" "void ordering_test()\n" "{\n" " tuple t2(5, 3.3f);\n" " BOOST_CHECK(t3 > t2);\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (style, inconclusive) The template instantiation 't2' hides a typedef with the same name.\n", errout.str()); checkSimplifyTypedef("class MyOverflowingUnsigned\n" "{\n" "public:\n" " typedef unsigned self_type::* bool_type;\n" " operator bool_type() const { return this->v_ ? &self_type::v_ : 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); checkSimplifyTypedef("typedef int (*fptr_type)(int, int);\n" "struct which_one {\n" " typedef fptr_type (*result_type)(bool x);\n" "}"); ASSERT_EQUALS("", errout.str()); checkSimplifyTypedef("class my_configuration\n" "{\n" "public:\n" " template < typename T >\n" " class hook\n" " {\n" " public:\n" " typedef ::boost::rational rational_type;\n" " public:\n" " rational_type ( &r_ )[ 9 ];\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); checkSimplifyTypedef("class A\n" "{\n" " typedef B b;\n" " friend b;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef41() { // ticket #1488 checkSimplifyTypedef("class Y;\n" "class X\n" "{\n" " typedef Y type;\n" " friend class type;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef42() { // ticket #1506 checkSimplifyTypedef("typedef struct A { } A;\n" "struct A;"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) The struct 'A' forward declaration is unnecessary. Type struct is already declared earlier.\n", errout.str()); checkSimplifyTypedef("typedef union A { int i; float f; } A;\n" "union A;"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) The union 'A' forward declaration is unnecessary. Type union is already declared earlier.\n", errout.str()); const char code [] = "typedef std::map A;\n" "class A;"; checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) The class 'A' forward declaration is unnecessary. Type class is already declared earlier.\n", errout.str()); TODO_ASSERT_EQUALS("class A ;", "class std :: map < std :: string , int > ;", tok(code)); } void simplifyTypedef43() { // ticket #1588 { const char code[] = "typedef struct foo A;\n" "struct A\n" "{\n" " int alloclen;\n" "};\n"; // The expected result.. const std::string expected("struct A " "{ " "int alloclen ; " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style, inconclusive) The struct 'A' hides a typedef with the same name.\n", errout.str()); } { const char code[] = "typedef union foo A;\n" "union A\n" "{\n" " int alloclen;\n" "};\n"; // The expected result.. const std::string expected("union A " "{ " "int alloclen ; " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style, inconclusive) The union 'A' hides a typedef with the same name.\n", errout.str()); } { const char code[] = "typedef class foo A;\n" "class A\n" "{\n" " int alloclen;\n" "};\n"; // The expected result.. const std::string expected("class A " "{ " "int alloclen ; " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style, inconclusive) The class 'A' hides a typedef with the same name.\n", errout.str()); } } void simplifyTypedef44() { { const char code[] = "typedef std::map Map;\n" "class MyMap : public Map\n" "{\n" "};\n"; // The expected result.. const std::string expected("class MyMap : public std :: map < std :: string , int > " "{ " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef std::map Map;\n" "class MyMap : protected Map\n" "{\n" "};\n"; // The expected result.. const std::string expected("class MyMap : protected std :: map < std :: string , int > " "{ " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef std::map Map;\n" "class MyMap : private Map\n" "{\n" "};\n"; // The expected result.. const std::string expected("class MyMap : private std :: map < std :: string , int > " "{ " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef struct foo { } A;\n" "struct MyA : public A\n" "{\n" "};\n"; // The expected result.. const std::string expected("struct foo { } ; " "struct MyA : public foo " "{ " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef class foo { } A;\n" "class MyA : public A\n" "{\n" "};\n"; // The expected result.. const std::string expected("class foo { } ; " "class MyA : public foo " "{ " "} ;"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(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;\n"; // The expected result.. const std::string 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 std::string 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 std::string 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" "}\n"; // The expected result.. const std::string 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 std::string 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 std::string 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 std::string expected("class A { public: int i ; } ; " "const char ( A :: * 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 std::string expected("char ( * foo ( ) ) [ 10 ] { }"); ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(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 simplifyTypedef53() { // ticket #1801 { const char code[] = "typedef int ( * int ( * ) ( ) ) ( ) ;"; // this is invalid C so just make sure it doesn't crash checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:1]: (debug) Failed to parse 'typedef int ( * int ( * ) ( ) ) ( ) ;'. The checking continues anyway.\n", errout.str()); } { const char code[] = "typedef int (*PPDMarkOption)(ppd_file_t *ppd, const char *keyword, const char *option);\n" "typedef int (*PPDMarkOption)(ppd_file_t *ppd, const char *keyword, const char *option);\n"; checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style, inconclusive) The typedef 'PPDMarkOption' hides a typedef with the same name.\n", errout.str()); } { const char code[] = "typedef int * A;\n" "typedef int * A;\n"; checkSimplifyTypedef(code); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style, inconclusive) The typedef 'A' hides a typedef with the same name.\n", 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;\n"; // The expected result.. const std::string expected("long * v1 ; " "void * v2 [ 2 ] ; " "int * * 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" "};\n"; // The expected result.. const std::string expected("struct C { " "" "const void * pr ; " // this gets simplified to a regular pointer "operatorconstvoid(*)()& ( ) { return pr ; } " "} ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(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" "};\n"; // The expected result.. const std::string expected("void foo { " "" "int a ; a = int ( 1 ) * int ( 2 ) ; " "} ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(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};\n"; // The expected result.. const std::string expected("int coords [ 4 ] [ 2 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(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};\n"; // The expected result.. const std::string expected("int coords [ 4 ] [ 5 ] [ 7 ] [ 2 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedef59() { // ticket #2011 const char code[] = "template class SomeTemplateClass {\n" " typedef void (SomeTemplateClass::*MessageDispatcherFunc)(SerialInputMessage&);\n" "};\n"; // The expected result.. const std::string expected("template < typename DISPATCHER > class SomeTemplateClass { } ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(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" "}\n"; // The expected result.. const std::string expected("void f ( ) { int b ; int * f ; }"); ASSERT_EQUALS(expected, tok(code, false)); // Check for output.. checkSimplifyTypedef(code); 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);\n"; // 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);\n"; // 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 std::string expected1("void f ( ) { char a [ 256 ] ; char b [ 256 ] ; }"); ASSERT_EQUALS(expected1, tok(code1, false)); // Check for output.. checkSimplifyTypedef(code1); 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 std::string expected2("void f ( ) { char a [ 256 ] = { 0 } ; char b [ 256 ] = { 0 } ; }"); ASSERT_EQUALS(expected2, tok(code2, false)); // Check for output.. checkSimplifyTypedef(code2); ASSERT_EQUALS("", errout.str()); const char code3[] = "typedef char TString[256];\n" "void f()\n" "{\n" " TString a = \"\", b = \"\";\n" "}"; // The expected tokens.. const std::string expected3("void f ( ) { char a [ 256 ] = \"\" ; char b [ 256 ] = \"\" ; }"); ASSERT_EQUALS(expected3, tok(code3, false)); // Check for output.. checkSimplifyTypedef(code3); 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 std::string expected4("void f ( ) { char a [ 256 ] = \"1234\" ; char b [ 256 ] = \"5678\" ; }"); ASSERT_EQUALS(expected4, tok(code4, false)); // Check for output.. checkSimplifyTypedef(code4); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef63() { // ticket #2175 'typedef float x[3];' const char code[] = "typedef float x[3];\n" "x a,b,c;\n"; 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;\n"; 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)); 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" "}\n"; const std::string 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) ();\n"; 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;\n"; const std::string 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);\n"; const std::string 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]);\n"; const std::string expected = "int ( * ( efuncs [ 1 ] ) ) ( ) ;"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef int RexxFunctionHandler();\n" "RexxFunctionHandler *(efuncs[]) = { NULL, NULL };\n"; const std::string expected = "int ( * ( efuncs [ ] ) ) ( ) = { 0 , 0 } ;"; 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" "};\n"; const std::string 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" "};\n"; const std::string 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); }\n"; const std::string 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 { }\n"; const std::string 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;\n"; const std::string 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);\n"; const std::string expected = "long ( * ( * current_state ) ( void ) ) ( void ) = 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; };\n"; ASSERT_EQUALS("", tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef76() { // ticket #2453 segmentation fault const char code[] = "void f1(typedef int x) {}\n"; const std::string expected = "void f1 ( typedef int x ) { }"; ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef77() { // ticket #2554 const char code[] = "typedef char Str[10]; int x = sizeof(Str);\n"; const std::string 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;\n"; const std::string 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;\n"; const std::string 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" "};\n"; const std::string expected = "struct s { } ; " "void f ( ) { " "sizeof ( struct s ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(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) { }\n"; // The expected result.. const std::string expected("void f ( char ( & cl ) [ 10 ] ) { }"); ASSERT_EQUALS(expected, tok(code)); } void simplifyTypedef84() { // ticket #2630 (segmentation fault) const char code1[] = "typedef y x () x\n"; ASSERT_THROW(checkSimplifyTypedef(code1), InternalError); const char code2[] = "typedef struct template <>\n"; ASSERT_THROW(checkSimplifyTypedef(code2), InternalError); const char code3[] = "typedef ::<>\n"; ASSERT_THROW(checkSimplifyTypedef(code3), InternalError); } void simplifyTypedef85() { // ticket #2651 const char code[] = "typedef FOO ((BAR)(void, int, const int, int*));\n"; const char expected[] = ";"; checkSimplifyTypedef(code); 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" "};\n"; const char expected[] = "class relational { " "" "public: " "operatorsafe_bool ( ) const ; " "safe_bool operator! ( ) const ; " "} ;"; checkSimplifyTypedef(code); ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef87() { // ticket #2651 const char code[] = "typedef FOO (*(*BAR)(void, int, const int, int*));\n"; const char expected[] = ";"; checkSimplifyTypedef(code); ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef88() { // ticket #2675 const char code[] = "typedef short int (*x)(...);\n"; const char expected[] = ";"; checkSimplifyTypedef(code); 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" "};\n"; const char expected[] = "class Fred { void func ( int ) const ; } ;"; checkSimplifyTypedef(code); 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&) {};\n"; const char expected[] = "void f ( const int ( & ) [ 2 ] ) { } ;"; checkSimplifyTypedef(code); 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" "}\n"; const char expected1[] = "namespace NS { " "" "class A { " "int * f ( ) ; " "} ; " "} " "namespace NS { " "int * A :: f ( ) { } " "}"; checkSimplifyTypedef(code1); 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() {}\n"; const char expected2[] = "namespace NS { " "" "class A { " "int * f ( ) ; " "} ; " "} " "int * NS :: A :: f ( ) { }"; checkSimplifyTypedef(code2); 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" "}\n"; const char expected3[] = "namespace NS1 { " "namespace NS2 { " "" "class A { " "int * f ( ) ; " "} ; " "} " "} " "namespace NS1 { " "namespace NS2 { " "int * A :: f ( ) { } " "} " "}"; checkSimplifyTypedef(code3); 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" "}\n"; const char expected4[] = "namespace NS1 { " "namespace NS2 { " "" "class A { " "int * f ( ) ; " "} ; " "} " "} " "namespace NS1 { " "int * NS2 :: A :: f ( ) { } " "}"; checkSimplifyTypedef(code4); 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" "}\n"; checkSimplifyTypedef(code); 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);\n"; const char expected[] = "struct s { double x ; } ;"; checkSimplifyTypedef(code); 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;\n"; const char expected1[] = "class A { " "public: " "struct data { " "int a [ 4 ] ; " "} ; " "} ; " "struct A :: data d ;"; checkSimplifyTypedef(code1); ASSERT_EQUALS(expected1, tok(code1)); TODO_ASSERT_EQUALS("[test.cpp:7]: (debug) Scope::checkVariable found variable 'd' with varid 0.\n", "", errout.str()); const char code2[] = "class A {\n" "public:\n" " typedef struct {\n" " int a[4];\n" " } data;\n" "};\n" "::A::data d;\n"; const char expected2[] = "class A { " "public: " "struct data { " "int a [ 4 ] ; " "} ; " "} ; " "struct :: A :: data d ;"; checkSimplifyTypedef(code2); ASSERT_EQUALS(expected2, tok(code2)); TODO_ASSERT_EQUALS("[test.cpp:7]: (debug) Scope::checkVariable found variable 'd' with varid 0.\n", "", 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 { };\n"; const char expected3[] = "class A { " "public: " "struct data { " "int a [ 4 ] ; " "} ; " "} ; " "class B : public :: A :: data { } ;"; checkSimplifyTypedef(code3); 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" "};\n"; const char expected[] = "class symbol_table { " "public: " "expression_error :: error_code * f ; " "} ;"; checkSimplifyTypedef(code); ASSERT_EQUALS(expected, tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef96() { // ticket #2886 (segmentation fault) const char code[] = "typedef struct x { }\n"; ASSERT_THROW(tok(code), InternalError); } void simplifyTypedef97() { // ticket #2983 (segmentation fault) const char code[] = "typedef x y\n" "(A); y\n"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef98() { // ticket #2963 const char code[] = "typedef int type ## __LINE__;\n" "typedef int type ## __LINE__;\n" "type1 x;\n" "type2 y;"; ASSERT_EQUALS("int x ; int y ;", tok(code)); } void simplifyTypedef99() { // ticket #2999 const char code[] = "typedef struct Fred Fred;\n" "struct Fred { };\n"; tok(code); ASSERT_EQUALS("", errout.str()); const char code1[] = "struct Fred { };\n" "typedef struct Fred Fred;\n"; 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" "}\n"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef101() { // ticket #3003 (segmentation fault) const char code[] = "typedef a x[];\n" "y = x\n"; ASSERT_THROW(tok(code), InternalError); } void simplifyTypedef102() { // ticket #3004 const char code[] = "typedef struct { } Fred;\n" "void foo()\n" "{\n" " Fred * Fred;\n" "}\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" "}\n"; tok(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef104() { // ticket #3070 const char code[] = "typedef int (*in_func) (void FAR *, unsigned char FAR * FAR *);\n"; ASSERT_EQUALS(";", tok(code)); ASSERT_EQUALS("", errout.str()); } void simplifyTypedef105() { // ticket #3616 (segmentation fault) const char code[] = "( int typedef char x; ){}\n"; 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 simplifyTypedefFunction1() { { const char code[] = "typedef void (*my_func)();\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (*my_func)(void);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( void ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (*my_func)(int);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (*my_func)(int*);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(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 std::string expected("std :: queue < void ( * ) ( arg_class * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)();\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(void);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( void ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(int);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(int*);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func)(arg_class*);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( arg_class * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func();\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(void);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( void ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(int);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(int*);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void my_func(arg_class*);\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( arg_class * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func());\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(void));\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( void ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(int));\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(int*));\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( int * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } { const char code[] = "typedef void (my_func(arg_class*));\n" "std::queue func_queue;"; // The expected result.. const std::string expected("std :: queue < void ( * ) ( arg_class * ) > func_queue ;"); ASSERT_EQUALS(expected, tok(code)); // Check for output.. checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } } void simplifyTypedefFunction2() { // ticket #1685 const char code[] = "typedef void voidfn (int);\n" "voidfn xxx;"; // The expected result.. const std::string 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 std::string expected("C f1 ( ) ; " "C * f2 ; " // this gets simplified to a regular pointer "C ( & f3 ) ( ) ; " "C * f4 ; " "C ( C :: * f5 ) ( ) const ; " "C * f6 ; " // volatile is removed "C ( C :: * f7 ) ( ) const ;"); // volatile is removed ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); 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 std::string expected("const C f1 ( ) ; " "const C * f2 ; " // this gets simplified to a regular pointer "const C ( & f3 ) ( ) ; " "const C * f4 ; " "const C ( C :: * f5 ) ( ) const ; " "const C * f6 ; " // volatile is removed "const C ( C :: * f7 ) ( ) const ;"); // volatile is removed ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); 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 std::string expected("const C f1 ( ) ; " "const C * f2 ; " // this gets simplified to a regular pointer "const C ( & f3 ) ( ) ; " "const C * f4 ; " "const C ( C :: * f5 ) ( ) const ; " "const C * f6 ; " // volatile is removed "const C ( C :: * f7 ) ( ) const ;"); // volatile is removed ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); 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 std::string expected("C * f1 ( ) ; " "C * * f2 ; " // this gets simplified to a regular pointer "C * ( & f3 ) ( ) ; " "C * * f4 ; " "C * ( C :: * f5 ) ( ) const ; " "C * * f6 ; " // volatile is removed "C * ( C :: * f7 ) ( ) const ;"); // volatile is removed ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); 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 std::string expected("const C * f1 ( ) ; " "const C * * f2 ; " // this gets simplified to a regular pointer "const C * ( & f3 ) ( ) ; " "const C * * f4 ; " "const C * ( C :: * f5 ) ( ) const ; " "const C * * f6 ; " // volatile is removed "const C * ( C :: * f7 ) ( ) const ;"); // volatile is removed ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); 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 std::string expected("const C * f1 ( ) ; " "const C * * f2 ; " // this gets simplified to a regular pointer "const C * ( & f3 ) ( ) ; " "const C * * f4 ; " "const C * ( C :: * f5 ) ( ) const ; " "const C * * f6 ; " // volatile is removed "const C * ( C :: * f7 ) ( ) const ;"); // volatile is removed ASSERT_EQUALS(expected, tok(code)); checkSimplifyTypedef(code); 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 std::string expected("int ( * ( * t1 ) ( bool ) ) ( int , int ) ; " "int * t2 ( bool ) ; " "int * t3 ( bool ) ;"); ASSERT_EQUALS(expected, tok(code, false)); checkSimplifyTypedef(code); 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 std::string 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)); checkSimplifyTypedef(code); 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 ; }\n"; // The expected result.. const std::string 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)); checkSimplifyTypedef(code); ASSERT_EQUALS("", errout.str()); } void simplifyTypedefFunction7() { const char code[] = "typedef void ( __gnu_cxx :: _SGIAssignableConcept < _Tp > :: * _func_Tp_SGIAssignableConcept ) () ;" "_func_Tp_SGIAssignableConcept X;\n"; // The expected result.. const std::string expected("void ( __gnu_cxx :: _SGIAssignableConcept < _Tp > :: * X ) ( ) ;"); ASSERT_EQUALS(expected, tok(code, false)); checkSimplifyTypedef(code); 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))){}\n"; 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 simplifyTypedefShadow() { // shadow variable (#4445) const char code[] = "typedef struct { int x; } xyz;;\n" "void f(){\n" " int abc, xyz;\n" // <- shadow variable "}\n"; ASSERT_EQUALS("struct xyz { int x ; } ; void f ( ) { int abc ; int xyz ; }", tok(code,false)); } 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 reverseArraySyntax() { ASSERT_EQUALS("a [ 13 ]", tok("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)); } } 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)); } } void pointeralias1() { { const char code[] = "void f()\n" "{\n" " char buf[100];\n" " char *p = buf;\n" " free(p);\n" "}\n"; const char expected[] = "void f ( ) " "{ " "char buf [ 100 ] ; " "free ( buf ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { 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)); } { const char code[] = "int *foo()\n" "{\n" " int a[10];\n" " int *b = a;\n" " return b;\n" "}\n"; const char expected[] = "int * foo ( ) " "{ " "int a [ 10 ] ; " "return a ; " "}"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "void f() {\n" " int a[10];\n" " int *b = a;\n" " memset(b,0,sizeof(a));\n" "}"; const char expected[] = "void f ( ) {" " int a [ 10 ] ;" " memset ( a , 0 , 40 ) ; " "}"; 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[] = "void f()\n" "{\n" " int a[10];\n" " int *p = &a[0];\n" " *p = 0;\n" "}\n"; const char expected[] = "void f ( ) " "{" " int a [ 10 ] ;" " * a = 0 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void pointeralias5() { 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 reduceConstness() { ASSERT_EQUALS("char * p ;", tok("char * const p;")); } 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 ( ) { }", tok("void f() { int i; for (i = 0; i < 0; i++) { a; } }")); //ticket #3140 ASSERT_EQUALS("void f ( ) { }", tok("void f() { int i; for (i = 0; i < 0; i++) { foo(); break; } }")); ASSERT_EQUALS("void f ( ) { }", 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; } }")); } void while1() { // ticket #1197 const char code[] = "void do {} while (0) { }"; const char expected[] = "void { }"; ASSERT_EQUALS(expected, tok(code)); } void enum1() { const char code[] = "enum A { a, b, c }; A c1 = c;"; const char expected[] = "int c1 ; c1 = 2 ;"; ASSERT_EQUALS(expected, tok(code, false)); } void enum2() { const char code[] = "enum A { a, }; int array[a];"; const char expected[] = "int array [ 0 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } void enum3() { const char code[] = "enum { a, }; int array[a];"; const char expected[] = "int array [ 0 ] ;"; ASSERT_EQUALS(expected, tok(code, false)); } void enum4() { { const char code[] = "class A {\n" "public:\n" " enum EA { a1, a2, a3 };\n" " EA get() const;\n" " void put(EA a) { ea = a; ea = a1; }\n" "private:\n" " EA ea;\n" "};\n" "A::EA A::get() const { return ea; }\n" "A::EA e = A::a1;"; const char expected[] = "class A { " "public: " "" "int get ( ) const ; " "void put ( int a ) { ea = a ; ea = 0 ; } " "private: " "int ea ; " "} ; " "int A :: get ( ) const { return ea ; } " "int e ; e = 0 ;"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "struct A {\n" " enum EA { a1, a2, a3 };\n" " EA get() const;\n" " void put(EA a) { ea = a; ea = a1; }\n" " EA ea;\n" "};\n" "A::EA A::get() const { return ea; }\n" "A::EA e = A::a1;"; const char expected[] = "struct A { " "" "int get ( ) const ; " "void put ( int a ) { ea = a ; ea = 0 ; } " "int ea ; " "} ; " "int A :: get ( ) const { return ea ; } " "int e ; e = 0 ;"; ASSERT_EQUALS(expected, tok(code, false)); } } void enum5() { const char code[] = "enum ABC {\n" " a = sizeof(int),\n" " b = 1 + a,\n" " c = b + 100,\n" " d,\n" " e,\n" " f = 90,\n" " g\n" "};\n" "int sum = a + b + c + d + e + f + g;"; const char expected[] = "int sum ; sum = " "sizeof ( int ) + " "( 1 + sizeof ( int ) ) + " "( 1 + sizeof ( int ) + 100 ) + " // 101 = 100 + 1 "( 1 + sizeof ( int ) + 101 ) + " // 102 = 100 + 1 + 1 "( 1 + sizeof ( int ) + 102 ) + 181 " // 283 = 100+2+90+91 ";"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("int sum ; sum = 508 ;", tok(code, true)); } void enum6() { const char code[] = "enum { a = MAC(A, B, C) }; void f(a) { }"; const char expected[] = "void f ( a ) { }"; ASSERT_EQUALS(expected, tok(code, false)); } void enum7() { { // ticket 1388 const char code[] = "enum FOO {A,B,C};\n" "int main()\n" "{\n" " int A = B;\n" " { float A = C; }\n" "}"; const char expected[] = "int main ( ) " "{ " "int A ; A = 1 ; " "{ float A ; A = 2 ; } " "}"; ASSERT_EQUALS(expected, tok(code, false)); } { const char code[] = "enum FOO {A,B,C};\n" "void f(int A, float B, char C) { }"; const char expected[] = "void f ( int A , float B , char C ) { }"; ASSERT_EQUALS(expected, tok(code, false)); } } // Check simplifyEnum std::string checkSimplifyEnum(const char code[], bool cpp = true) { errout.str(""); // Tokenize.. Settings settings; settings.addEnabled("style"); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, cpp?"test.cpp":"test.c"); return tokenizer.tokens()->stringifyList(0, true); } void enum8() { // ticket 1388 checkSimplifyEnum("enum Direction {N=100,E,S,W,ALL};\n" "template class EF_Vector{\n" " T v_v[S];\n" "\n" "public:\n" " EF_Vector();\n" " explicit EF_Vector(const T &);\n" " explicit EF_Vector(const T arr[S]);\n" "};\n" "\n" "template\n" "EF_Vector::EF_Vector()\n" "{\n" "}\n" "\n" "template\n" "EF_Vector::EF_Vector(const T &t)\n" "{\n" " for(int i=0;i\n" "EF_Vector::EF_Vector(const T arr[S])\n" "{\n" " for(int i=0;i d;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) Template parameter 'S' hides enumerator with same name\n" "[test.cpp:11] -> [test.cpp:1]: (style) Template parameter 'S' hides enumerator with same name\n" "[test.cpp:16] -> [test.cpp:1]: (style) Template parameter 'S' hides enumerator with same name\n" "[test.cpp:23] -> [test.cpp:1]: (style) Template parameter 'S' hides enumerator with same name\n", errout.str()); } void enum9() { // ticket 1404 checkSimplifyEnum("class XX {\n" "public:\n" "static void Set(const int &p){m_p=p;}\n" "static int m_p;\n" "};\n" "int XX::m_p=0;\n" "int main() {\n" " enum { XX };\n" " XX::Set(std::numeric_limits::digits());\n" "}"); ASSERT_EQUALS("", errout.str()); } void enum10() { // ticket 1445 const char code[] = "enum {\n" "SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,\n" "} e = SHELL_SIZE;"; const char expected[] = "int e ; e = sizeof ( union { int i ; char * cp ; double d ; } ) - 1 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); ASSERT_EQUALS("", errout.str()); } void enum11() { const char code[] = "int main()\n" "{\n" " enum { u, v };\n" " A u = 1, v = 2;\n" "}"; const char expected[] = "int main ( ) " "{ " "" "A u ; u = 1 ; A v ; v = 2 ; " "}"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style) Variable 'u' hides enumerator with same name\n" "[test.cpp:4] -> [test.cpp:3]: (style) Variable 'v' hides enumerator with same name\n", errout.str()); } void enum12() { const char code[] = "enum fred { a, b };\n" "void foo()\n" "{\n" " unsigned int fred = 0;\n" "}"; const char expected[] = "void foo ( ) { unsigned int fred ; fred = 0 ; }"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } void enum13() { const char code[] = "enum ab { ENTRY(1, a = 0), ENTRY(2, b) };\n" "void foo()\n" "{\n" " unsigned int fred = a;\n" "}"; const char expected[] = "void foo ( ) { unsigned int fred ; fred = a ; }"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } void enum14() { const char code[] = "enum ab { a };\n" "ab"; const char expected[] = "ab"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } void enum15() { // C++0x features { const char code[] = "enum : char { a = 99 };\n" "char c1 = a;"; const char expected[] = "char c1 ; c1 = 99 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum class Enum1 { a };\n" "Enum1 e1 = Enum1::a;"; const char expected[] = "int e1 ; e1 = 0 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum class Enum1 { a };\n" "Enum1 e1 = a;"; const char expected[] = "int e1 ; e1 = a ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum Enum1 : char { a };\n" "Enum1 e1 = a;"; const char expected[] = "char e1 ; e1 = 0 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum class Enum1 : unsigned char { a };\n" "Enum1 e1 = Enum1::a;"; const char expected[] = "unsigned char e1 ; e1 = 0 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum class Enum1 : unsigned int { a };\n" "Enum1 e1 = Enum1::a;"; const char expected[] = "unsigned int e1 ; e1 = 0 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum class Enum1 : unsigned long long int { a };\n" "Enum1 e1 = Enum1::a;"; const char expected[] = "unsigned long long e1 ; e1 = 0 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); } { const char code[] = "enum class { A };\n" "int i = A;"; const char expected [] = "int i ; i = 0 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code, false)); // Compile as C code: enum has name 'class' checkSimplifyEnum(code, true); // Compile as C++ code: Don't crash } } void enum16() { // ticket #1988 const char code[] = "enum D : auto * { FF = 0 };"; ASSERT_THROW(checkSimplifyEnum(code), InternalError); } void enum17() { // ticket #2381 // if header is included twice its enums will be duplicated const char code[] = "enum ab { a=0, b };" "enum ab { a=0, b };\n"; ASSERT_EQUALS(";", checkSimplifyEnum(code)); ASSERT_EQUALS("", errout.str()); } void enum18() { // ticket #2466 - array with same name as enum constant const char code[] = "enum ab { a=0, b };\n" "void f() { a[0]; }\n"; ASSERT_EQUALS("void f ( ) { a [ 0 ] ; }", checkSimplifyEnum(code)); } void enum19() { // ticket #2536 const char code[] = "enum class E1;\n" "enum class E2 : int;\n"; ASSERT_EQUALS(";", checkSimplifyEnum(code)); } void enum20() { // ticket #2600 segmentation fault const char code[] = "enum { const }\n"; ASSERT_EQUALS("", checkSimplifyEnum(code)); } void enum21() { // ticket #2720 syntax error const char code[] = "enum E2 : signed const short { };\n"; ASSERT_EQUALS(";", checkSimplifyEnum(code)); ASSERT_EQUALS("", errout.str()); } void enum22() { // ticket #2745 const char code[] = "enum en { x = 0 };\n" "void f() {\n" " int x = 0;\n" " g(x);\n" "}\n" "void f2(int &x) {\n" " x+=1;\n" "}\n"; checkSimplifyEnum(code); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (style) Variable 'x' hides enumerator with same name\n" "[test.cpp:6] -> [test.cpp:1]: (style) Function argument 'x' hides enumerator with same name\n", errout.str()); // avoid false positive: in other scope const char code2[] = "class C1 { enum en { x = 0 }; };\n" "class C2 { bool x; };\n"; checkSimplifyEnum(code2); ASSERT_EQUALS("", errout.str()); // avoid false positive: inner if-scope const char code3[] = "enum en { x = 0 };\n" "void f() { if (aa) ; else if (bb==x) df; }\n"; checkSimplifyEnum(code3); ASSERT_EQUALS("", errout.str()); } void enum23() { // ticket #2804 const char code[] = "enum Enumerator : std::uint8_t { ITEM1, ITEM2, ITEM3 };\n" "Enumerator e = ITEM3;\n"; const char expected[] = "std :: uint8_t e ; e = 2 ;"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); ASSERT_EQUALS("", errout.str()); } void enum24() { // ticket #2828 const char code[] = "enum EnumName { STYLE = 0x0001 };\n" "void f(long style) {\n" " if (style & STYLE) { }\n" "}\n"; const char expected[] = "void f ( long style ) { if ( style & 1 ) { } }"; ASSERT_EQUALS(expected, checkSimplifyEnum(code)); ASSERT_EQUALS("", errout.str()); } void enum25() { // ticket #2966 (segmentation fault) const char code[] = "enum x :\n"; ASSERT_THROW(checkSimplifyEnum(code), InternalError); } void enum26() { // ticket #2975 (segmentation fault) const char code[] = "enum E {} e enum\n"; checkSimplifyEnum(code); ASSERT_EQUALS("", errout.str()); } void enum27() { // ticket #3005 (segmentation fault) const char code[] = "enum : x\n"; ASSERT_THROW(checkSimplifyEnum(code), InternalError); } void enum28() { const char code[] = "enum { x=0 };\n" "void f() { char x[4]; memset(x, 0, 4);\n" "{ x } };\n" "void g() { x; }"; ASSERT_EQUALS("void f ( ) { char x [ 4 ] ; memset ( x , 0 , 4 ) ; { x } } ; void g ( ) { 0 ; }", checkSimplifyEnum(code)); } void enum29() { // #3747 - bitwise or value const char code[] = "enum { x=1, y=x|2 }; i = (3==y);"; ASSERT_EQUALS("i = 3 == 3 ;", checkSimplifyEnum(code)); } void enum30() { // #3852 - false positive const char code [] = "class TestIf\n" "{\n" "public:\n" " enum class Foo\n" " {\n" " one = 0,\n" " two = 1\n" " };\n" " enum class Bar\n" " {\n" " one = 0,\n" " two = 1\n" " };\n" "};\n" "int main() {" " return TestIf::Bar::two;\n" "}"; ASSERT_EQUALS("class TestIf { public: } ; int main ( ) { return 1 ; }", checkSimplifyEnum(code)); ASSERT_EQUALS("", errout.str()); } void enum31() { // #3934 - calculation in first item const char code[] = "enum { x=2*32, y }; i = y;"; ASSERT_EQUALS("i = 65 ;", checkSimplifyEnum(code)); } void enum32() { // #3998 - wrong enum simplification => access violation const char code[] = "enum { x=(32), y=x, z }; { a, z }"; ASSERT_EQUALS("{ a , ( 33 ) }", checkSimplifyEnum(code)); } void enum33() { // #4015 - segmentation fault const char code[] = "enum { A=SOME_VALUE, B=A };"; ASSERT_EQUALS(";", checkSimplifyEnum(code)); } void enum34() { // #4141 - division by zero const char code[] = "enum { A=1/0 };"; ASSERT_EQUALS(";", checkSimplifyEnum(code)); } void enum35() { // #3953 - avoid simplification of type ASSERT_EQUALS("void f ( A * a ) ;", checkSimplifyEnum("enum { A }; void f(A * a) ;")); ASSERT_EQUALS("void f ( A * a ) { }", checkSimplifyEnum("enum { A }; void f(A * a) { }")); } void enum36() { // #4378 const char code[] = "struct X { enum Y { a, b }; X(Y) { Y y = (Y)1; } };"; ASSERT_EQUALS("struct X { X ( int ) { int y ; y = ( int ) 1 ; } } ;", checkSimplifyEnum(code)); } void enum37() { // #4280 - shadow variables const char code1[] = "enum { a, b }; void f(int a) { return a + 1; }"; ASSERT_EQUALS("void f ( int a ) { return a + 1 ; }", checkSimplifyEnum(code1)); const char code2[] = "enum { a, b }; void f() { int a; }"; ASSERT_EQUALS("void f ( ) { int a ; }", checkSimplifyEnum(code2)); const char code3[] = "enum { a, b }; void f() { int *a=do_something(); }"; ASSERT_EQUALS("void f ( ) { int * a ; a = do_something ( ) ; }", checkSimplifyEnum(code3)); const char code4[] = "enum { a, b }; void f() { int &a=x; }"; ASSERT_EQUALS("void f ( ) { int & a = x ; }", checkSimplifyEnum(code4)); // #4857 - not shadow variable checkSimplifyEnum("enum { a,b }; void f() { if (x) { } else if ( x & a ) {} }"); ASSERT_EQUALS("", errout.str()); } void enum38() { // #4463 const char code[] = "enum { a,b }; void f() { throw a; }"; checkSimplifyEnum(code); ASSERT_EQUALS("", errout.str()); } void enum39() { // #5145 - fp variable hides enum const char code[] = "enum { A }; void f() { int a = 1 * A; }"; checkSimplifyEnum(code); ASSERT_EQUALS("", errout.str()); } void enum40() { const char code[] = "enum { A=(1<<0)|(1<<1) }; void f() { x = y + A; }"; ASSERT_EQUALS("void f ( ) { x = y + ( 3 ) ; }", checkSimplifyEnum(code)); } void enum41() { // ticket #5212 (valgrind errors during enum simplification) const char code[] = "namespace Foo {\n" " enum BarConfig {\n" " eBitOne = (1 << 0),\n" " eBitTwo = (1 << 1),\n" " eAll = eBitOne|eBitTwo\n" " };\n" "}\n" "int x = Foo::eAll;"; ASSERT_EQUALS("int x ; x = ( 1 ) | 2 ;", checkSimplifyEnum(code)); } void enum42() { // ticket #5182 (template function call in template value) const char code[] = "enum { A = f() };\n" "a = A;"; ASSERT_EQUALS("a = f < int , 2 > ( ) ;", checkSimplifyEnum(code)); } void enum43() { // lhs in assignment const char code[] = "enum { A, B };\n" "A = 1;"; ASSERT_EQUALS("A = 1 ;", checkSimplifyEnum(code)); } void enumscope1() { // #3949 - don't simplify enum from one function in another function const char code[] = "void foo() { enum { A = 0, B = 1 }; }\n" "void bar() { int a = A; }"; ASSERT_EQUALS("void foo ( ) { } void bar ( ) { int a ; a = A ; }", checkSimplifyEnum(code)); } void duplicateDefinition() { // #3565 - wrongly detects duplicate definition const Settings settings; Tokenizer tokenizer(&settings, 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, tokenizer.tokens())); } void invalid_enum() { // #5600: missing include causes invalid enum const char code [] = "enum {\n" " NUM_OPCODES = \n" // #include "definition" "};\n" "struct bytecode {};\n" "jv jq_next() { opcode = ((opcode) +NUM_OPCODES);\n" "}"; ASSERT_THROW(checkSimplifyEnum(code), InternalError); } 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++; }")); } 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 initstruct() { ASSERT_EQUALS("; struct A a ; a . buf = 3 ;", tok("; struct A a = { .buf = 3 };")); ASSERT_EQUALS("; struct A a ; a . buf = x ;", tok("; struct A a = { .buf = x };")); ASSERT_EQUALS("; struct A a ; a . buf = & key ;", tok("; struct A a = { .buf = &key };")); ASSERT_EQUALS("; struct ABC abc ; abc . a = 3 ; abc . b = x ; abc . c = & key ;", tok("; struct ABC abc = { .a = 3, .b = x, .c = &key };")); TODO_ASSERT_EQUALS("; struct A a ; a . buf = { 0 } ;", "; struct A a ; a = { . buf = { 0 } } ;", tok("; struct A a = { .buf = {0} };")); } 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)); } } 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 simplifyStructDecl5() { const char code[] = "\n" "{\n" " struct {\n" " typename D4:typename Base\n" " };\n" "};\n"; //don't crash 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 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() override ; };", 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("if ( a ) { }", tok("if ( likely ( a ) ) { }", true)); ASSERT_EQUALS("if ( a ) { }", tok("if ( unlikely ( a ) ) { }", 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)); } 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)); ASSERT_EQUALS("int f ( ) ;", tok("int APIENTRY f();", true)); ASSERT_EQUALS("int f ( ) ;", tok("int CALLBACK f();", true)); } 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)); } } void removeVoidFromFunction() { ASSERT_EQUALS("void foo ( ) ;", tok("void foo(void);")); } void removeUnnecessaryQualification1() { const char code[] = "class Fred { Fred::Fred() {} };"; const char expected[] = "class Fred { Fred ( ) { } } ;"; ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS("[test.cpp:1]: (portability) The extra qualification 'Fred::' is unnecessary and is considered an error by many compilers.\n", errout.str()); } void removeUnnecessaryQualification2() { const char code[] = "template\n" "struct grammar : qi::grammar {\n" " grammar() : grammar::base_type(start) { }\n" "};\n"; tok(code, false); ASSERT_EQUALS("", errout.str()); } void removeUnnecessaryQualification3() { const char code[] = "namespace one {\n" " class c {\n" " public:\n" " void function() {}\n" " };\n" "}\n" "namespace two {\n" " class c : public one::c {\n" " public:\n" " void function() {\n" " one::c::function();\n" " }\n" " };\n" "}\n"; tok(code, false); ASSERT_EQUALS("", errout.str()); } void removeUnnecessaryQualification4() { const char code[] = "namespace one {\n" " class c {\n" " public:\n" " void function() {}\n" " };\n" "}\n" "class c : public one::c {\n" "public:\n" " void function() {\n" " one::c::function();\n" " }\n" "};\n"; tok(code, false); ASSERT_EQUALS("", errout.str()); } void removeUnnecessaryQualification5() { const char code[] = "namespace one {\n" " class c {\n" " public:\n" " void function() {}\n" " };\n" "}\n" "namespace two {\n" " class c : public one::c {\n" " public:\n" " void function() {\n" " two::c::function();\n" " }\n" " };\n" "}\n"; tok(code, false); ASSERT_EQUALS("[test.cpp:11]: (portability) The extra qualification 'two::c::' is unnecessary and is considered an error by many compilers.\n", errout.str()); } void removeUnnecessaryQualification6() { const char code[] = "namespace NS {\n" " int HRDF_bit() { return 1; }\n" " void HRDF_bit_set() { }\n" " void func(int var) {\n" " if (!NS::HRDF_bit())\n" " return;\n" " else\n" " NS::HRDF_bit_set();\n" " }\n" "}\n"; tok(code, false); ASSERT_EQUALS("", errout.str()); } void removeUnnecessaryQualification7() { // ticket #2970 const char code[] = "class TProcedure {\n" "public:\n" " TProcedure::TProcedure(long endAddress) : m_lEndAddr(endAddress){}\n" "private:\n" " long m_lEndAddr;\n" "};\n"; tok(code, false); ASSERT_EQUALS("[test.cpp:3]: (portability) The extra qualification 'TProcedure::' is unnecessary and is considered an error by many compilers.\n", errout.str()); } void removeUnnecessaryQualification8() { const char code[] = "class Fred {\n" "public:\n" " Fred & Fred::operator = (const Fred &);\n" " void Fred::operator () (void);\n" " void Fred::operator delete[](void* x);\n" "};\n"; tok(code, false); ASSERT_EQUALS("[test.cpp:3]: (portability) The extra qualification 'Fred::' is unnecessary and is considered an error by many compilers.\n" "[test.cpp:4]: (portability) The extra qualification 'Fred::' is unnecessary and is considered an error by many compilers.\n" "[test.cpp:5]: (portability) The extra qualification 'Fred::' is unnecessary and is considered an error by many compilers.\n", errout.str()); } void removeUnnecessaryQualification9() { const char code[] = "class Fred {\n" "public:\n" " Fred::~Fred();\n" "};\n"; tok(code, false); ASSERT_EQUALS("[test.cpp:3]: (portability) The extra qualification 'Fred::' is unnecessary and is considered an error by many compilers.\n", errout.str()); } void removeUnnecessaryQualification10() { const char code[] = "template class A\n" "{\n" " operator T();\n" " A() { T (A::*f)() = &A::operator T; }\n" "};\n"; tok(code, false); ASSERT_EQUALS("", errout.str()); } void simplifyIfNotNull() { { // ticket # 2601 segmentation fault const char code[] = "|| #if #define <="; tok(code, false); ASSERT_EQUALS("", errout.str()); } { const char code[] = "void f(int x) {\n" " x = (x != 0);\n" "}"; ASSERT_EQUALS("void f ( int x ) { }", tok(code, false)); } } void simplifyVarDecl1() { // ticket # 2682 segmentation fault const char code[] = "x a[0] ="; tok(code, false); ASSERT_EQUALS("", errout.str()); } void simplifyVarDecl2() { // ticket # 2834 segmentation fault const char code[] = "std::vector::iterator"; tok(code, false); ASSERT_EQUALS("", errout.str()); } 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)); } } 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[]) { }")); } 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)); // Don't crash tok("int", 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\nworld\"[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.66/test/testsizeof.cpp000066400000000000000000000564711236713773000171360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checksizeof.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestSizeof : public TestFixture { public: TestSizeof() : TestFixture("TestSizeof") { } private: void run() { TEST_CASE(sizeofsizeof); TEST_CASE(sizeofCalculation); TEST_CASE(checkPointerSizeof); 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(""); Settings settings; settings.addEnabled("warning"); settings.addEnabled("portability"); settings.inconclusive = true; // 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 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()); } 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]: (error) 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]: (error) 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]: (error) Using 'sizeof' on array given as " "function argument returns size of a pointer.\n", 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]: (error) 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]: (error) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) 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, inconclusive) Size of pointer 'buf1' 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()); } 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* 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 = (void *)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()); } 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.66/test/teststl.cpp000066400000000000000000003021361236713773000164310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkstl.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestStl : public TestFixture { public: TestStl() : TestFixture("TestStl") { } private: void run() { 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); // #5598 invalid code causing a crash TEST_CASE(dereference); TEST_CASE(dereference_break); // #3644 - handle "break" TEST_CASE(dereference_member); TEST_CASE(STLSize); TEST_CASE(STLSizeNoErr); 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(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(insert1); TEST_CASE(insert2); TEST_CASE(stlBoundaries1); TEST_CASE(stlBoundaries2); TEST_CASE(stlBoundaries3); TEST_CASE(stlBoundaries4); // #4364 TEST_CASE(stlBoundaries5); // #4352 // 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(dereference_auto); TEST_CASE(readingEmptyStlContainer); } void check(const char code[], const bool inconclusive=false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); settings.addEnabled("style"); settings.addEnabled("performance"); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckStl checkStl; 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()); // 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()); } 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()); } 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(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" " v++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 foo() { struct }; template struct S { Used x; void bar() } auto f = [this] { }; } };"); 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()); } 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()); } 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" " 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()); } 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 erase1() { check("void f()\n" "{\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:3] -> [test.cpp:4]: (error) Iterator 'it' used after element has been erased.\n" "[test.cpp:6] -> [test.cpp:7]: (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()); // #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 (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 (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 (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()); } 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" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Dangerous iterator usage after erase()-method.\n", "", errout.str()); } void eraseGoto() { check("void f()\n" "{\n" " for (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 (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 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 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 size_t getArraylength(const T(&)[n]) { return n; } void stlBoundaries1() { const std::string stlCont[] = { "list", "set", "multiset", "map", "multimap", "hash_map", "hash_multimap", "hash_set" }; 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("[test.cpp:4]: (error) Dangerous iterator comparison using operator< on 'std::" + stlCont[i] + "'.\n", errout.str()); } 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 iterator comparison using operator< on 'std::forward_list'.\n", errout.str()); // #5926 no FP Dangerous iterator comparison using operator< 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 iterator comparison using operator< on 'std::forward_list'.\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 iterator comparison using operator< on 'std::forward_list'.\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) Invalid iterator 'i' used.\n", 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 (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()); // 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()); // --------------------------- // 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()); // #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 (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()); } void size1() { check("struct Fred {\n" " void foo();\n" " std::list x;\n" "};\n" "void Fred::foo()\n" "{\n" " if (x.size() == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("std::list x;\n" "void f()\n" "{\n" " if (x.size() == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (0 == x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (0 != x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() > 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (0 < x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() >= 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size() < 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (1 <= x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (1 > x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " if (!x.size()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " fun(x.size());\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " fun(!x.size());\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("void f()\n" "{\n" " std::list x;\n" " fun(a && x.size());\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Possible inefficient checking for 'x' emptiness.\n", 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() { check("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" "}"); ASSERT_EQUALS("[test.cpp:10]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); } void size3() { check("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" "}"); ASSERT_EQUALS("[test.cpp:10]: (performance) Possible inefficient checking for 'x' emptiness.\n", errout.str()); check("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" "}"); ASSERT_EQUALS("[test.cpp:10]: (performance) Possible inefficient checking for 'x' emptiness.\n", 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()); } 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("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("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()); } 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 #2967 (segmentation fault) check("auto_ptr\n"); 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()); } void uselessCalls() { check("void f()\n" "{\n" " 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" " 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()); 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::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:2]: (warning) Return value of std::remove() ignored. Elements remain in container.\n" "[test.cpp:3]: (warning) Return value of std::remove_if() ignored. Elements remain in container.\n" "[test.cpp:4]: (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 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\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\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\n", errout.str()); check("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::vector v) {\n" " v.clear();\n" " int i = v.find(foobar);\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Reading from empty STL container\n", 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\n", errout.str()); } }; REGISTER_TEST(TestStl) cppcheck-1.66/test/testsuite.cpp000066400000000000000000000231231236713773000167540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include std::ostringstream errout; std::ostringstream output; std::ostringstream warnings; /** * 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 std::string &_name) :classname(_name) ,gcc_style_errors(false) ,quiet_tests(false) { TestRegistry::theInstance().addTest(this); } bool TestFixture::runTest(const char testname[]) { if (testToRun.empty() || testToRun == testname) { ++countTests; if (quiet_tests) { std::cout << '.'; } 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 *filename, unsigned int linenr, bool condition) const { if (!condition) { ++fails_counter; if (gcc_style_errors) { errmsg << filename << ':' << linenr << ": Assertion failed." << std::endl; } else { errmsg << "Assertion failed in " << filename << " at line " << linenr << std::endl << "_____" << std::endl; } } } void TestFixture::assertEquals(const char *filename, unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg) const { if (expected != actual) { ++fails_counter; if (gcc_style_errors) { errmsg << filename << ':' << linenr << ": Assertion failed. " << "Expected: " << writestr(expected, true) << ". Actual: " << writestr(actual, true) << '.' << std::endl; if (!msg.empty()) errmsg << msg << std::endl; } else { errmsg << "Assertion failed in " << filename << " at line " << linenr << 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; } } } void TestFixture::assertEquals(const char *filename, 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 *filename, 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 *filename, 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 *filename, unsigned int linenr, long long expected, long long actual, const std::string &msg) const { std::ostringstream ostr1; ostr1 << expected; std::ostringstream ostr2; ostr2 << actual; assertEquals(filename, linenr, ostr1.str(), ostr2.str(), msg); } void TestFixture::assertEqualsDouble(const char *filename, unsigned int linenr, double expected, double actual, const std::string &msg) const { std::ostringstream ostr1; ostr1 << expected; std::ostringstream ostr2; ostr2 << actual; assertEquals(filename, linenr, ostr1.str(), ostr2.str(), msg); } void TestFixture::todoAssertEquals(const char *filename, unsigned int linenr, const std::string &wanted, const std::string ¤t, const std::string &actual) const { if (wanted == actual) { if (gcc_style_errors) { errmsg << filename << ':' << linenr << ": Assertion succeeded unexpectedly. " << "Result: " << writestr(wanted, true) << "." << std::endl; } else { errmsg << "Assertion succeeded unexpectedly in " << filename << " at line " << linenr << std::endl << "Result:" << std::endl << writestr(wanted) << std::endl << "_____" << std::endl; } ++succeeded_todos_counter; } else { assertEquals(filename, linenr, current, actual); ++todos_counter; } } void TestFixture::todoAssertEquals(const char *filename, unsigned int linenr, long long wanted, long long current, 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::assertThrowFail(const char *filename, unsigned int linenr) const { ++fails_counter; if (gcc_style_errors) { errmsg << filename << ':' << linenr << " Assertion failed. " << "The expected exception was not thrown" << std::endl; } else { errmsg << "Assertion failed in " << filename << " at line " << linenr << std::endl << "The expected exception was not thrown" << std::endl << "_____" << std::endl; } } void TestFixture::complainMissingLib(const char* libname) const { missingLibs.insert(libname); } void TestFixture::run(const std::string &str) { testToRun = str; if (quiet_tests) { std::cout << '\n' << classname << ':'; } run(); } void TestFixture::warn(const char msg[]) { warnings << "Warning: " << currentTest << " " << msg << std::endl; } void TestFixture::processOptions(const options& args) { quiet_tests = args.quiet(); gcc_style_errors = args.gcc_style_errors(); } 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); } } const std::string &w(warnings.str()); if (!w.empty()) { std::cout << "\n\n" << w; } 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.66/test/testsuite.h000066400000000000000000000112441236713773000164220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 #include #include "errorlogger.h" #include "redirect.h" #include "library.h" 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: Library _lib; std::string classname; std::string testToRun; bool gcc_style_errors; bool quiet_tests; std::string currentTest; virtual void run() = 0; bool runTest(const char testname[]); void assert_(const char *filename, unsigned int linenr, bool condition) const; void todoAssert(const char *filename, unsigned int linenr, bool condition) const; void assertEquals(const char *filename, unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; void assertEquals(const char *filename, unsigned int linenr, const char expected[], const std::string& actual, const std::string &msg = emptyString) const; void assertEquals(const char *filename, unsigned int linenr, const char expected[], const char actual[], const std::string &msg = emptyString) const; void assertEquals(const char *filename, unsigned int linenr, const std::string& expected, const char actual[], const std::string &msg = emptyString) const; void assertEquals(const char *filename, unsigned int linenr, long long expected, long long actual, const std::string &msg = emptyString) const; void assertEqualsDouble(const char *filename, unsigned int linenr, double expected, double actual, const std::string &msg = emptyString) const; void todoAssertEquals(const char *filename, unsigned int linenr, const std::string &wanted, const std::string ¤t, const std::string &actual) const; void todoAssertEquals(const char *filename, unsigned int linenr, long long wanted, long long current, long long actual) const; void assertThrowFail(const char *filename, unsigned int linenr) const; void complainMissingLib(const char* libname) 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); void warn(const char msg[]); TestFixture(const std::string &_name); virtual ~TestFixture() { } static std::size_t runTests(const options& args); }; #define TEST_CASE( NAME ) if ( runTest(#NAME) ) { _lib = Library(); currentTest = classname + "::" + #NAME; if (quiet_tests) { REDIRECT; NAME(); } else { NAME ();} } #define ASSERT( CONDITION ) assert_(__FILE__, __LINE__, CONDITION) #define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_EQUALS_DOUBLE( EXPECTED , ACTUAL ) assertEqualsDouble(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_EQUALS_MSG( EXPECTED , ACTUAL, MSG ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL, MSG) #define ASSERT_THROW( CMD, EXCEPTION ) try { CMD ; assertThrowFail(__FILE__, __LINE__); } catch (EXCEPTION &) { } catch (...) { assertThrowFail(__FILE__, __LINE__); } #define TODO_ASSERT_EQUALS( WANTED , CURRENT , ACTUAL ) todoAssertEquals(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL) #define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; } #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 #define LOAD_LIB( NAME ) { LOAD_LIB_2(_lib, NAME); } #endif cppcheck-1.66/test/testsuppressions.cpp000066400000000000000000000325761236713773000204140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "settings.h" #include "testsuite.h" #include "cppcheckexecutor.h" #include "threadexecutor.h" #include extern std::ostringstream errout; 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(suppressionWithRelativePaths); // #4733 } 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)); } // Check the suppression void checkSuppression(const char code[], 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"); if (!suppression.empty()) { std::string r = settings.nomsg.addSuppressionLine(suppression); ASSERT_EQUALS("", r); } cppCheck.check("test.cpp", code); reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions()); } void 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); executor.check(); reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions()); } // Check the suppression for multiple files void checkSuppression(const char *names[], const char *codes[], 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"); if (!suppression.empty()) settings.nomsg.addSuppressionLine(suppression); for (int i = 0; names[i] != NULL; ++i) cppCheck.check(names[i], codes[i]); reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions()); } void runChecks(void (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, 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()); } void suppressionsSettings() { runChecks(&TestSuppressions::checkSuppression); if (ThreadExecutor::isEnabled()) runChecks(&TestSuppressions::checkSuppressionThreads); } void suppressionsMultiFile() { const char *names[] = {"abc.cpp", "xyz.cpp", NULL}; const char *codes[] = { "void f() {\n" "}\n", "void f() {\n" " int a;\n" " a++;\n" "}\n", }; // suppress uninitvar for this file and line checkSuppression(names, codes, "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)); } void inlinesuppress_unusedFunction() const { // #4210 - wrong report of "unmatchedSuppression" for "unusedFunction" Suppressions suppressions; suppressions.addSuppression("unusedFunction", "test.c", 3U); ASSERT_EQUALS(true, suppressions.getUnmatchedLocalSuppressions("test.c").empty()); ASSERT_EQUALS(false, suppressions.getUnmatchedGlobalSuppressions().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()); } }; REGISTER_TEST(TestSuppressions) cppcheck-1.66/test/testsymboldatabase.cpp000066400000000000000000003032001236713773000206120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testutils.h" #include "symboldatabase.h" #include #define GET_SYMBOL_DB(code) \ errout.str(""); \ Settings settings; \ Tokenizer tokenizer(&settings, this); \ std::istringstream istr(code); \ tokenizer.tokenize(istr, "test.cpp"); \ const SymbolDatabase *db = tokenizer.getSymbolDatabase(); #define GET_SYMBOL_DB_C(code) \ errout.str(""); \ Settings settings; \ Tokenizer tokenizer(&settings, this); \ std::istringstream istr(code); \ tokenizer.tokenize(istr, "test.c"); \ const SymbolDatabase *db = tokenizer.getSymbolDatabase(); class TestSymbolDatabase: public TestFixture { public: TestSymbolDatabase() :TestFixture("TestSymbolDatabase") ,si(nullptr, nullptr, nullptr) ,vartok(nullptr) ,typetok(nullptr) ,t(nullptr) ,found(false) { } private: void createSymbolDatabase(const char code[]) { errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.getSymbolDatabase(); } const Scope si; const Token* vartok; const Token* typetok; const Token* t; bool found; void reset() { vartok = nullptr; typetok = nullptr; t = nullptr; found = false; } 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() { TEST_CASE(array); TEST_CASE(test_isVariableDeclarationCanHandleNull); TEST_CASE(test_isVariableDeclarationIdentifiesSimpleDeclaration); 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_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(isVariableDeclarationPointerConst); TEST_CASE(isVariableDeclarationRValueRef); TEST_CASE(isVariableStlType); TEST_CASE(arrayMemberVar1); TEST_CASE(arrayMemberVar2); TEST_CASE(arrayMemberVar3); TEST_CASE(staticMemberVar); TEST_CASE(hasRegularFunction); TEST_CASE(hasInlineClassFunction); TEST_CASE(hasMissingInlineClassFunction); TEST_CASE(hasClassFunction); TEST_CASE(hasRegularFunctionReturningFunctionPointer); TEST_CASE(hasInlineClassFunctionReturningFunctionPointer); TEST_CASE(hasMissingInlineClassFunctionReturningFunctionPointer); TEST_CASE(hasClassFunctionReturningFunctionPointer); TEST_CASE(complexFunctionArrayPtr); TEST_CASE(pointerToMemberFunction); TEST_CASE(hasSubClassConstructor); TEST_CASE(testConstructors); TEST_CASE(functionDeclarationTemplate); TEST_CASE(functionDeclarations); TEST_CASE(memberFunctionOfUnknownClassMacro1); TEST_CASE(memberFunctionOfUnknownClassMacro2); TEST_CASE(memberFunctionOfUnknownClassMacro3); 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(functionArgs1); TEST_CASE(functionArgs2); TEST_CASE(functionArgs3); TEST_CASE(functionArgs4); TEST_CASE(namespaces1); TEST_CASE(namespaces2); TEST_CASE(namespaces3); // #3854 - unknown macro 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(symboldatabase15); // ticket #2591 TEST_CASE(symboldatabase16); // ticket #2637 TEST_CASE(symboldatabase17); // ticket #2657 TEST_CASE(symboldatabase18); // ticket #2865 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(symboldatabase39); // ticket #5120 (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(isImplicitlyVirtual); TEST_CASE(garbage); TEST_CASE(isFunction); // UNKNOWN_MACRO(a,b) { .. } TEST_CASE(findFunction1); TEST_CASE(findFunction2); // mismatch: parameter passed by address => reference argument 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); // ticket #5867 } void array() const { std::istringstream code("int a[10+2];"); TokenList list(nullptr); list.createTokens(code, "test.c"); list.front()->tokAt(2)->link(list.front()->tokAt(6)); Variable v(list.front()->next(), list.front(), list.back(), 0, Public, nullptr, nullptr); ASSERT(v.isArray()); ASSERT_EQUALS(1U, v.dimensions().size()); ASSERT_EQUALS(0U, 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); } 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); ASSERT(true == v.isArray()); ASSERT(false == v.isPointer()); 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); ASSERT(true == v.isArray()); ASSERT(true == v.isPointer()); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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 isVariableDeclarationPointerConst() { reset(); givenACodeSampleToTokenize var("std::string const* s;"); bool result = si.isVariableDeclaration(var.tokens(), vartok, typetok); ASSERT_EQUALS(true, result); Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0); 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); ASSERT(false == v.isArray()); ASSERT(false == v.isPointer()); ASSERT(true == v.isReference()); ASSERT(true == v.isRValueReference()); ASSERT(var.tokens()->tokAt(2)->scope() != 0); } 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); const char* types[] = { "string", "wstring" }; const char* no_types[] = { "set" }; ASSERT_EQUALS(true, v.isStlType()); ASSERT_EQUALS(true, v.isStlType(types)); ASSERT_EQUALS(false, v.isStlType(no_types)); } { 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); const char* types[] = { "bitset", "set", "vector", "wstring" }; const char* no_types[] = { "bitset", "map", "set" }; ASSERT_EQUALS(true, v.isStlType()); ASSERT_EQUALS(true, v.isStlType(types)); ASSERT_EQUALS(false, v.isStlType(no_types)); } { 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); const char* types[] = { "bitset", "set", "vector" }; ASSERT_EQUALS(false, v.isStlType()); ASSERT_EQUALS(false, v.isStlType(types)); } } void arrayMemberVar1() { const char code[] = "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 "}"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), ". x"); tok = tok ? tok->next() : nullptr; ASSERT(tok && tok->variable() && Token::simpleMatch(tok->variable()->typeStartToken(), "int x ;")); ASSERT(tok && tok->varId() == 0U); // It's possible to set a varId } void arrayMemberVar2() { const char code[] = "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 "}"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), ". x"); tok = tok ? tok->next() : nullptr; ASSERT(tok && tok->variable() && Token::simpleMatch(tok->variable()->typeStartToken(), "int x ;")); ASSERT(tok && tok->varId() == 0U); // It's possible to set a varId } void arrayMemberVar3() { const char code[] = "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 "}"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token *tok = Token::findsimplematch(tokenizer.tokens(), ". x"); tok = tok ? tok->next() : nullptr; ASSERT(tok && tok->variable() && Token::simpleMatch(tok->variable()->typeStartToken(), "int x ;")); ASSERT(tok && tok->varId() == 0U); // 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 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"); ASSERT(scope && 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 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"); ASSERT(scope && 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 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 hasClassFunction() { GET_SYMBOL_DB("class Fred { void func(); }; 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"); ASSERT(scope && 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 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) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(2) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(3) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(4) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(5) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(6) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(7) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(8) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(9) != nullptr); ASSERT_EQUALS(true, db->getVariableFromVarId(10) != nullptr); 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(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(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(Foo&& f); };"); 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 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() == ""); 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() == ""); 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() == ""); if (db) { const Scope *scope = db->findScopeByName("getFormula1"); ASSERT(scope != nullptr); ASSERT(scope && scope->nestedIn == &db->scopeList.front()); } } 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 check(const char code[], bool debug = true) { // Clear the error log errout.str(""); // Check.. Settings settings; settings.debugwarnings = debug; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // force symbol database creation tokenizer.getSymbolDatabase(); } 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* 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 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 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); check(str, false); 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[]) { }"); ASSERT_EQUALS("", 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("", 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 symboldatabase15() { // ticket #2591 - segmentation fault ASSERT_THROW(check("struct A :\n"), InternalError); } void symboldatabase16() { // ticket #2637 - segmentation fault check("{} const const\n"); ASSERT_EQUALS("", errout.str()); } void symboldatabase17() { // ticket #2657 - segmentation fault check("return f(){}"); ASSERT_EQUALS("", errout.str()); } void symboldatabase18() { // ticket #2865 - segmentation fault check("char a[1]\n"); 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); check(str, false); 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); check(str, false); 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); ASSERT(db && db->isClassOrStruct("Foo")); ASSERT(db && db->isClassOrStruct("Bar")); ASSERT(db && db->isClassOrStruct("Sub")); 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); ASSERT(db && db->isClassOrStruct("Fred")); ASSERT(db && db->isClassOrStruct("Wilma")); ASSERT(db && db->isClassOrStruct("Barney")); 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 symboldatabase39() { // ticket #5120 check("struct V : { public case {} ; struct U : U void { V *f (int x) (x) } }"); } 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); const Scope * const fscope = db ? db->findScopeByName("f") : nullptr; ASSERT(!!fscope); 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 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)); 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 garbage() { { GET_SYMBOL_DB("void f( { u = 1 ; } ) { }"); (void)db; } { GET_SYMBOL_DB("{ }; void namespace A::f; { g() { int } }"); (void)db; } { ASSERT_THROW(createSymbolDatabase("class Foo {}; class Bar : public Foo"), InternalError); } { ASSERT_THROW(createSymbolDatabase("YY_DECL { switch (yy_act) {\n" " case 65: YY_BREAK\n" " case YY_STATE_EOF(block):\n" " yyterminate(); \n" "} }"), InternalError); // #5663 } } 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, "%var% (") && !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 } #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) const Function *x = findFunctionByName(#x, y); \ ASSERT_EQUALS(true, x != nullptr); \ if (x) ASSERT_EQUALS(true, 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" "};"); 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); CLASS_FUNC(func2, fred); CLASS_FUNC(func3, fred); CLASS_FUNC(func4, fred); CLASS_FUNC(func5, fred); CLASS_FUNC(func6, fred); CLASS_FUNC(func7, fred); CLASS_FUNC(func8, fred); CLASS_FUNC(func9, fred); CLASS_FUNC(func10, fred); CLASS_FUNC(func11, fred); CLASS_FUNC(func12, fred); } } } 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); } } } #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->isDeclspecNothrow()); } } 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(true, b->isIntegralType()); ASSERT_EQUALS(false, b->isFloatingType()); } const Variable *c = db->getVariableFromVarId(2); ASSERT(c != nullptr); if (c) { ASSERT_EQUALS("c", c->nameToken()->str()); ASSERT_EQUALS(true, c->isIntegralType()); ASSERT_EQUALS(false, c->isFloatingType()); } const Variable *uc = db->getVariableFromVarId(3); ASSERT(uc != nullptr); if (uc) { ASSERT_EQUALS("uc", uc->nameToken()->str()); ASSERT_EQUALS(true, uc->isIntegralType()); ASSERT_EQUALS(false, uc->isFloatingType()); } const Variable *s = db->getVariableFromVarId(4); ASSERT(s != nullptr); if (s) { ASSERT_EQUALS("s", s->nameToken()->str()); ASSERT_EQUALS(true, s->isIntegralType()); ASSERT_EQUALS(false, s->isFloatingType()); } const Variable *us = db->getVariableFromVarId(5); ASSERT(us != nullptr); if (us) { ASSERT_EQUALS("us", us->nameToken()->str()); ASSERT_EQUALS(true, us->isIntegralType()); ASSERT_EQUALS(false, us->isFloatingType()); } const Variable *i = db->getVariableFromVarId(6); ASSERT(i != nullptr); if (i) { ASSERT_EQUALS("i", i->nameToken()->str()); ASSERT_EQUALS(true, i->isIntegralType()); ASSERT_EQUALS(false, i->isFloatingType()); } const Variable *u = db->getVariableFromVarId(7); ASSERT(u != nullptr); if (u) { ASSERT_EQUALS("u", u->nameToken()->str()); ASSERT_EQUALS(true, u->isIntegralType()); ASSERT_EQUALS(false, u->isFloatingType()); } const Variable *ui = db->getVariableFromVarId(8); ASSERT(ui != nullptr); if (ui) { ASSERT_EQUALS("ui", ui->nameToken()->str()); ASSERT_EQUALS(true, ui->isIntegralType()); ASSERT_EQUALS(false, ui->isFloatingType()); } const Variable *l = db->getVariableFromVarId(9); ASSERT(l != nullptr); if (l) { ASSERT_EQUALS("l", l->nameToken()->str()); ASSERT_EQUALS(true, l->isIntegralType()); ASSERT_EQUALS(false, l->isFloatingType()); } const Variable *ul = db->getVariableFromVarId(10); ASSERT(ul != nullptr); if (ul) { ASSERT_EQUALS("ul", ul->nameToken()->str()); ASSERT_EQUALS(true, ul->isIntegralType()); ASSERT_EQUALS(false, ul->isFloatingType()); } const Variable *ll = db->getVariableFromVarId(11); ASSERT(ll != nullptr); if (ll) { ASSERT_EQUALS("ll", ll->nameToken()->str()); ASSERT_EQUALS(true, ll->isIntegralType()); 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(false, f->isIntegralType()); ASSERT_EQUALS(true, f->isFloatingType()); } const Variable *d = db->getVariableFromVarId(2); ASSERT(d != nullptr); if (d) { ASSERT_EQUALS("d", d->nameToken()->str()); ASSERT_EQUALS(false, d->isIntegralType()); ASSERT_EQUALS(true, d->isFloatingType()); } const Variable *ld = db->getVariableFromVarId(3); ASSERT(ld != nullptr); if (ld) { ASSERT_EQUALS("ld", ld->nameToken()->str()); ASSERT_EQUALS(false, ld->isIntegralType()); 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(false, f->isIntegralType()); ASSERT_EQUALS(false, f->isFloatingType()); } const Variable *scf = db->getVariableFromVarId(2); ASSERT(scf != nullptr); if (scf) { ASSERT_EQUALS("scf", scf->nameToken()->str()); ASSERT_EQUALS(false, scf->isIntegralType()); ASSERT_EQUALS(false, scf->isFloatingType()); } } { 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(false, fa->isIntegralType()); ASSERT_EQUALS(false, fa->isFloatingType()); } } } 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->isIntegralType()); 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->isIntegralType()); 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()); } }; REGISTER_TEST(TestSymbolDatabase) cppcheck-1.66/test/testthreadexecutor.cpp000066400000000000000000000100661236713773000206530ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "threadexecutor.h" #include "cppcheckexecutor.h" #include #include extern std::ostringstream errout; extern std::ostringstream output; class TestThreadExecutor : public TestFixture { public: TestThreadExecutor() : TestFixture("TestThreadExecutor") { } private: /** * 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 settings; 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() { 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() { std::ostringstream oss; oss << "int main()\n" << "{\n"; oss << " char *a = malloc(10);\n"; oss << " return 0;\n" << "}"; check(20, 100, 100, oss.str()); } void no_errors_more_files() { std::ostringstream oss; oss << "int main()\n" << "{\n" << " return 0;\n" << "}\n"; check(2, 3, 0, oss.str()); } void no_errors_less_files() { std::ostringstream oss; oss << "int main()\n" << "{\n" << " return 0;\n" << "}\n"; check(2, 1, 0, oss.str()); } void no_errors_equal_amount_files() { std::ostringstream oss; oss << "int main()\n" << "{\n" << " return 0;\n" << "}\n"; check(2, 2, 0, oss.str()); } void one_error_less_files() { std::ostringstream oss; oss << "int main()\n" << "{\n" << " {char *a = malloc(10);}\n" << " return 0;\n" << "}\n"; check(2, 1, 1, oss.str()); } void one_error_several_files() { std::ostringstream oss; oss << "int main()\n" << "{\n" << " {char *a = malloc(10);}\n" << " return 0;\n" << "}\n"; check(2, 20, 20, oss.str()); } }; REGISTER_TEST(TestThreadExecutor) cppcheck-1.66/test/testtimer.cpp000066400000000000000000000023541236713773000167460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #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.66/test/testtoken.cpp000066400000000000000000001062051236713773000167460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testutils.h" #include "token.h" #include "settings.h" #include #include extern std::ostringstream errout; 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(canFindMatchingBracketsNeedsOpen); TEST_CASE(canFindMatchingBracketsInnerPair); TEST_CASE(canFindMatchingBracketsOuterPair); TEST_CASE(canFindMatchingBracketsWithTooManyClosing); TEST_CASE(canFindMatchingBracketsWithTooManyOpening); } 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(), 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)); ASSERT_EQUALS(0, Token::multiCompare(¬found, "one||two", 0)); 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% %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% )|&&|%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 %var% xyz|%or% %var% ;")); ASSERT_EQUALS(false, Token::Match(toks.tokens(), "return %var% %or%|xyz %var% ;")); givenACodeSampleToTokenize toks2("return a | b ;", true); ASSERT_EQUALS(true, Token::Match(toks2.tokens(), "return %var% xyz|%or% %var% ;")); ASSERT_EQUALS(true, Token::Match(toks2.tokens(), "return %var% %or%|xyz %var% ;")); givenACodeSampleToTokenize toks3("return a || b ;", true); ASSERT_EQUALS(false, Token::Match(toks3.tokens(), "return %var% xyz|%or% %var% ;")); ASSERT_EQUALS(false, Token::Match(toks3.tokens(), "return %var% %or%|xyz %var% ;")); ASSERT_EQUALS(true, Token::Match(toks3.tokens(), "return %var% xyz|%oror% %var% ;")); ASSERT_EQUALS(true, Token::Match(toks3.tokens(), "return %var% %oror%|xyz %var% ;")); givenACodeSampleToTokenize toks4("a % b ;", true); ASSERT_EQUALS(true, Token::Match(toks4.tokens(), "%var% >>|<<|&|%or%|^|% %var% ;")); ASSERT_EQUALS(true, Token::Match(toks4.tokens(), "%var% %|>>|<<|&|%or%|^ %var% ;")); ASSERT_EQUALS(true, Token::Match(toks4.tokens(), "%var% >>|<<|&|%or%|%|^ %var% ;")); //%var%|%num% support givenACodeSampleToTokenize num("100", true); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%num%|%var%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%var%|%num%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%var%|%num%|%bool%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%var%|%bool%|%num%")); ASSERT_EQUALS(true, Token::Match(num.tokens(), "%var%|%bool%|%str%|%num%")); ASSERT_EQUALS(false, Token::Match(num.tokens(), "%bool%|%var%")); 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%|%var% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %var%|%num% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %var%|%num%|%bool% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %var%|%bool%|%num% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| %var%|%bool%|%str%|%num% )|")); ASSERT_EQUALS(false, Token::Match(numparen.tokens(), "(| %bool%|%var% )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %num%|%var%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %var%|%num%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %var%|%num%|%bool%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %var%|%bool%|%num%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %var%|%bool%|%str%|%num%| )|")); ASSERT_EQUALS(true, Token::Match(numparen.tokens(), "(| 100 %bool%|%var%| )|")); } void multiCompare4() const { givenACodeSampleToTokenize var("std :: queue < int > foo ;"); ASSERT_EQUALS(Token::eBracket, var.tokens()->tokAt(3)->type()); ASSERT_EQUALS(Token::eBracket, var.tokens()->tokAt(5)->type()); 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()); } 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(), "%var% %or% %var%")); givenACodeSampleToTokenize varLogOrVar("abc||def", true); ASSERT_EQUALS(true, Token::Match(varLogOrVar.tokens(), "%var% %oror% %var%")); } 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% %var%")); 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% %var%", 0),InternalError); ASSERT_EQUALS(true, Token::Match(var.tokens(), "%type% %varid% ; %type% %var%", 1)); ASSERT_EQUALS(true, Token::Match(var.tokens(), "%type% %var% ; %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)); } 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.type()); } for (test_op = logicalOps.begin(); test_op != logicalOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eLogicalOp, tok.type()); } for (test_op = bitOps.begin(); test_op != bitOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eBitOp, tok.type()); } for (test_op = comparisonOps.begin(); test_op != comparisonOps.end(); ++test_op) { Token tok(nullptr); tok.str(*test_op); ASSERT_EQUALS(Token::eComparisonOp, tok.type()); } Token tok(nullptr); tok.str("++"); ASSERT_EQUALS(Token::eIncDecOp, tok.type()); tok.str("--"); ASSERT_EQUALS(Token::eIncDecOp, tok.type()); } void literals() const { Token tok(nullptr); tok.str("\"foo\""); ASSERT(tok.type() == Token::eString); tok.str("\"\""); ASSERT(tok.type() == Token::eString); tok.str("'f'"); ASSERT(tok.type() == Token::eChar); tok.str("12345"); ASSERT(tok.type() == Token::eNumber); tok.str("-55"); ASSERT(tok.type() == Token::eNumber); tok.str("true"); ASSERT(tok.type() == Token::eBoolean); tok.str("false"); ASSERT(tok.type() == 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()); } 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 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 && t->str() == ")"); } }; REGISTER_TEST(TestToken) cppcheck-1.66/test/testtokenize.cpp000066400000000000000000017116661236713773000174740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "tokenize.h" #include "token.h" #include "settings.h" #include "path.h" #include "preprocessor.h" // usually tests here should not use preprocessor... #include #include #include extern std::ostringstream errout; class TestTokenizer : public TestFixture { public: TestTokenizer() : TestFixture("TestTokenizer") { } private: void run() { 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(tokenize10); TEST_CASE(tokenize11); TEST_CASE(tokenize12); TEST_CASE(tokenize13); // bailout if the code contains "@" - that is not handled well. TEST_CASE(tokenize14); // tokenize "0X10" => 16 TEST_CASE(tokenize15); // tokenize ".123" TEST_CASE(tokenize16); // #2612 - segfault for "<><<" 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(tokenize20); // replace C99 _Bool => bool TEST_CASE(tokenize21); // tokenize 0x0E-7 TEST_CASE(tokenize22); // special marker $ from preprocessor TEST_CASE(tokenize24); // #4195 (segmentation fault) TEST_CASE(tokenize25); // #4239 (segmentation fault) TEST_CASE(tokenize26); // #4245 (segmentation fault) TEST_CASE(tokenize27); // #4525 (segmentation fault) TEST_CASE(tokenize28); // #4725 (writing asm() around "^{}") TEST_CASE(tokenize29); // #5506 (segmentation fault upon invalid code) TEST_CASE(tokenize30); // #5356 (segmentation fault upon invalid code) 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) // 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(simplifyFileAndLineMacro); // tokenize "return - __LINE__;" TEST_CASE(foreach); // #3690 TEST_CASE(concatenateNegativeNumber); TEST_CASE(longtok); TEST_CASE(removeCast1); TEST_CASE(removeCast2); TEST_CASE(removeCast3); TEST_CASE(removeCast4); TEST_CASE(removeCast5); TEST_CASE(removeCast6); TEST_CASE(removeCast7); TEST_CASE(removeCast8); TEST_CASE(removeCast9); TEST_CASE(removeCast10); TEST_CASE(removeCast11); TEST_CASE(removeCast12); TEST_CASE(removeCast13); TEST_CASE(removeCast14); TEST_CASE(removeCast15); // #5996 - don't remove cast in 'a+static_cast(b?60:0)' TEST_CASE(simplifyFloatCasts); // float casting a integer TEST_CASE(inlineasm); TEST_CASE(ifAddBraces1); TEST_CASE(ifAddBraces2); TEST_CASE(ifAddBraces3); TEST_CASE(ifAddBraces4); TEST_CASE(ifAddBraces5); TEST_CASE(ifAddBraces6); TEST_CASE(ifAddBraces7); TEST_CASE(ifAddBraces9); TEST_CASE(ifAddBraces10); TEST_CASE(ifAddBraces11); TEST_CASE(ifAddBraces12); TEST_CASE(ifAddBraces13); TEST_CASE(ifAddBraces14); // #2610 - segfault: if()<{} TEST_CASE(ifAddBraces15); // #2616 - unknown macro before if TEST_CASE(ifAddBraces16); // ticket # 2739 (segmentation fault) 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(whileAddBraces); TEST_CASE(doWhileAddBraces); TEST_CASE(forAddBraces1); TEST_CASE(forAddBraces2); // #5088 TEST_CASE(pointers_condition); 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(simplifyKnownVariables24); TEST_CASE(simplifyKnownVariables25); TEST_CASE(simplifyKnownVariables26); 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(simplifyKnownVariablesIfEq1); // if (a==5) => a is 5 in the block TEST_CASE(simplifyKnownVariablesIfEq2); // if (a==5) { buf[a++] = 0; } TEST_CASE(simplifyKnownVariablesIfEq3); // #4708 - if (a==5) { buf[--a] = 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(simplifyKnownVariablesReturn); // 3500 - return TEST_CASE(simplifyExternC); TEST_CASE(simplifyKeyword); // #5842 - remove C99 static keyword between [] 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(varid31); // ticket #2831 (segmentation fault) TEST_CASE(varid32); // ticket #2835 (segmentation fault) TEST_CASE(varid33); // ticket #2875 (segmentation fault) TEST_CASE(varid34); // ticket #2825 TEST_CASE(varid35); // ticket #2937 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(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(varidStl); 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_initList); 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_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(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(varid_classnameshaddowsvariablename) // #3990 TEST_CASE(file1); TEST_CASE(file2); TEST_CASE(file3); TEST_CASE(line1); // Ticket #4408 TEST_CASE(line2); // Ticket #5423 TEST_CASE(doublesharp); TEST_CASE(isZeroNumber); TEST_CASE(isOneNumber); TEST_CASE(isTwoNumber); TEST_CASE(macrodoublesharp); 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(removeParentheses2); 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(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_null); TEST_CASE(simplifyMulAndParens); // Ticket #2784 + #3184 TEST_CASE(simplifyStructDecl); TEST_CASE(vardecl1); TEST_CASE(vardecl2); TEST_CASE(vardecl3); TEST_CASE(vardecl4); 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(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(volatile_variables); TEST_CASE(syntax_error); TEST_CASE(syntax_error_templates_1); TEST_CASE(syntax_error_templates_2); TEST_CASE(syntax_error_templates_3); // Ticket #5605, #5759, #5762, #5774 TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code TEST_CASE(removeKeywords); // 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(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(removeRedundantAssignment); TEST_CASE(removedeclspec); TEST_CASE(removeattribute); TEST_CASE(functionAttributeBefore); TEST_CASE(functionAttributeAfter); TEST_CASE(cpp0xtemplate1); TEST_CASE(cpp0xtemplate2); TEST_CASE(cpp0xtemplate3); 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(bitfields11); // ticket #2845 (segmentation fault) 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(simplifyNamespaceStd); TEST_CASE(microsoftMFC); TEST_CASE(microsoftMemory); TEST_CASE(borland); TEST_CASE(Qt); TEST_CASE(simplifySQL); TEST_CASE(simplifyCAlternativeTokens); TEST_CASE(simplifyCalculations); // foo(p = new char[10]); => p = new char[10]; foo(p); TEST_CASE(simplifyAssignmentInFunctionCall); // "x += .." => "x = x + .." TEST_CASE(simplifyCompoundAssignment); // 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(simplifyNull); 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(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(simplifyMathFunctions); // ticket #5031 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_div); TEST_CASE(simplifyMathFunctions_pow); TEST_CASE(simplifyMathFunctions_islessgreater); TEST_CASE(simplifyMathFunctions_islessequal); TEST_CASE(simplifyMathFunctions_isless); TEST_CASE(simplifyMathFunctions_isgreaterequal); TEST_CASE(simplifyMathFunctions_isgreater); 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(simplifyMathFunctions_fma); TEST_CASE(simplifyMathExpressions); //ticket #1620 TEST_CASE(simplifyStaticConst); TEST_CASE(compileLimits); // #5592 crash: gcc: testsuit: gcc.c-torture/compile/limits-declparen.c // AST data TEST_CASE(astexpr); 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(astGarbage); TEST_CASE(startOfExecutableScope); } std::string tokenizeAndStringify(const char code[], bool simplify = false, bool expand = true, Settings::PlatformType platform = Settings::Unspecified, const char* filename = "test.cpp", bool cpp11 = true) { errout.str(""); Settings settings; settings.debugwarnings = true; settings.platform(platform); settings.standards.cpp = cpp11 ? Standards::CPP11 : Standards::CPP03; // tokenize.. Tokenizer tokenizer(&settings, 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") == std::string::npos) errout << line << "\n"; } if (tokenizer.tokens()) return tokenizer.tokens()->stringifyList(false, expand, false, true, false, 0, 0); else return ""; } 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("\n\n##file 0\n" "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 tokenize10() { ASSERT_EQUALS("private:", tokenizeAndStringify("private:", false)); ASSERT_EQUALS("protected:", tokenizeAndStringify("protected:", false)); ASSERT_EQUALS("public:", tokenizeAndStringify("public:", false)); ASSERT_EQUALS("__published:", tokenizeAndStringify("__published:", false)); } void tokenize11() { ASSERT_EQUALS("X * sizeof ( Y ( ) ) ;", tokenizeAndStringify("X * sizeof(Y());", false)); } // ticket #2118 - invalid syntax error void tokenize12() { const char code[] = "Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" " for (unsigned i = 0 ; i < count; i++) {\n" " }\n" "});"; ASSERT_THROW(tokenizeAndStringify(code), InternalError); } // 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_EQUALS("", tokenizeAndStringify(code)); } // Ticket #2361: 0X10 => 16 void tokenize14() { ASSERT_EQUALS("; 16 ;", tokenizeAndStringify(";0x10;")); ASSERT_EQUALS("; 16 ;", tokenizeAndStringify(";0X10;")); ASSERT_EQUALS("; 292 ;", tokenizeAndStringify(";0444;")); } // 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 } // #2612 - segfault for "<><<" void tokenize16() { tokenizeAndStringify("<><<"); } 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();) {} }")); } void tokenize20() { // replace C99 _Bool => bool ASSERT_EQUALS("bool a ; a = true ;", tokenizeAndStringify("_Bool a = true;")); } 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)")); } // #4195 - segfault for "enum { int f ( ) { return = } r = f ( ) ; }" void tokenize24() { ASSERT_THROW(tokenizeAndStringify("enum { int f ( ) { return = } r = f ( ) ; }"), InternalError); } // #4239 - segfault for "f ( struct { int typedef T x ; } ) { }" void tokenize25() { tokenizeAndStringify("f ( struct { int typedef T x ; } ) { }"); } // #4245 - segfault void tokenize26() { tokenizeAndStringify("class x { protected : template < int y = } ;"); } 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));"); } // #4725 - ^{} void tokenize28() { ASSERT_EQUALS("void f ( ) { asm ( \"^{}\" ) ; }", tokenizeAndStringify("void f() { ^{} }")); ASSERT_EQUALS("void f ( ) { asm ( \"x(^{})\" ) ; }", tokenizeAndStringify("void f() { x(^{}); }")); ASSERT_EQUALS("; asm ( \"voidf^{return}intmain\" ) ; ( ) { }", tokenizeAndStringify("; void f ^ { return } int main ( ) { }")); } // #5506 - segmentation fault upon invalid code void tokenize29() { tokenizeAndStringify("A template < int { int = -1 ; } template < int N > struct B { int [ A < N > :: zero ] ; } ; B < 0 > b ;"); } // #5356 - segmentation fault upon invalid code void tokenize30() { tokenizeAndStringify("struct template struct B { }; B < 0 > b;"); } // #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)); } 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 ; ) )", tokenizeAndStringify(code, true)); ASSERT_EQUALS("", errout.str()); } { const char code[] ="struct A { template struct { }; };"; ASSERT_THROW(tokenizeAndStringify(code, true), InternalError); } { const char code[] ="enum ABC { A,B, typedef enum { C } };"; ASSERT_THROW(tokenizeAndStringify(code, true), InternalError); } { // #3314 - don't report syntax error. const char code[] ="struct A { typedef B::C (A::*f)(); };"; tokenizeAndStringify(code, true); ASSERT_EQUALS("[test.cpp:1]: (debug) Failed to parse 'typedef B :: C ( A :: * f ) ( ) ;'. The checking continues anyway.\n", errout.str()); } } 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.. tokenizeAndStringify(code); } 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" "}"; Settings settings; 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(5, e.token->linenr()); } } void wrong_syntax4() { // #3618 const char code[] = "typedef void (x) (int); return x&"; ASSERT_THROW(tokenizeAndStringify(code), InternalError); } void wrong_syntax_if_macro() { // #2518 #4171 ASSERT_THROW(tokenizeAndStringify("void f() { if MACRO(); }", false), InternalError); // #4668 - note there is no semicolon after MACRO() ASSERT_THROW(tokenizeAndStringify("void f() { if (x) MACRO() {} }", false), InternalError); // #4810 - note there is no semicolon after MACRO() ASSERT_THROW(tokenizeAndStringify("void f() { if (x) MACRO() else ; }", false), InternalError); } void wrong_syntax_class_x_y() { // #3585 const char code[] = "class x y { };"; errout.str(""); Settings settings; settings.addEnabled("information"); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.c"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("[test.c: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() { //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 '({ %var%|%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()); } //wrong syntax { ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case: z(); break;}}"), InternalError); ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case;: z(); break;}}"), InternalError); ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case {}: z(); break;}}"), InternalError); ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case 0?{1}:{2} : z(); break;}}"), InternalError); ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case 0?1;:{2} : z(); break;}}"), InternalError); ASSERT_THROW(tokenizeAndStringify("void f() {switch (n) { case 0?(1?{3:4}):2 : z(); break;}}"), InternalError); //ticket #4234 ASSERT_THROW(tokenizeAndStringify("( ) { switch break ; { switch ( x ) { case } y break ; : } }"), InternalError); //ticket #4267 ASSERT_THROW(tokenizeAndStringify("f ( ) { switch break; { switch ( x ) { case } case break; -6: ( ) ; } }"), InternalError); } } void garbageCode1() { tokenizeAndStringify("struct x foo_t; foo_t typedef y;"); } void garbageCode2() { //#4300 (segmentation fault) ASSERT_THROW(tokenizeAndStringify("enum { D = 1 struct { } ; } s.b = D;"), InternalError); } void garbageCode3() { //#4849 (segmentation fault in Tokenizer::simplifyStructDecl (invalid code)) ASSERT_THROW(tokenizeAndStringify("enum { D = 2 s ; struct y { x } ; } { s.a = C ; s.b = D ; }"), InternalError); } void garbageCode4() { // #4887 ASSERT_THROW(tokenizeAndStringify("void f ( ) { = a ; if ( 1 ) if = ( 0 ) ; }"), InternalError); } void garbageCode5() { // #5168 tokenizeAndStringify("( asm : ; void : );"); } void garbageCode6() { // #5214 tokenizeAndStringify("int b = ( 0 ? ? ) 1 : 0 ;", /*simplify=*/true); tokenizeAndStringify("int a = int b = ( 0 ? ? ) 1 : 0 ;", /*simplify=*/true); } void garbageCode7() { ASSERT_THROW(tokenizeAndStringify("1 (int j) { return return (c) * sizeof } y[1];", /*simplify=*/true), InternalError); tokenizeAndStringify("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(); }", true), InternalError); } void garbageCode9() { ASSERT_THROW(tokenizeAndStringify("enum { e = { } } ( ) { { enum { } } } { e } ", true), InternalError); } void simplifyFileAndLineMacro() { // tokenize 'return - __LINE__' correctly ASSERT_EQUALS("\"test.cpp\"", tokenizeAndStringify("__FILE__")); ASSERT_EQUALS("return -1 ;", tokenizeAndStringify("return - __LINE__;")); } 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 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'); errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(filedata); tokenizer.tokenize(istr, "test.cpp"); // Expected result.. ASSERT_EQUALS(filedata, tokenizer.tokens()->str()); } // Don’t remove "(int *)".. void removeCast1() { const char code[] = "int *f(int *);"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyCasts(); ASSERT_EQUALS("int * f ( int * ) ;", tokenizer.tokens()->stringifyList(0, false)); } // remove static_cast.. void removeCast2() { const char code[] = "t = (static_cast *>(&p));\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyCasts(); ASSERT_EQUALS("t = ( & p ) ;", tokenizer.tokens()->stringifyList(0, false)); } void removeCast3() { // 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 removeCast4() { // ticket #970 const char code[] = "if (a >= (unsigned)(b)) {}"; const char expected[] = "if ( a >= ( unsigned int ) b ) { }"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true)); } void removeCast5() { // ticket #1817 ASSERT_EQUALS("a . data = f ;", tokenizeAndStringify("a->data = reinterpret_cast(static_cast(f));", true)); } void removeCast6() { // ticket #2103 ASSERT_EQUALS("if ( ! x ) { ; }", tokenizeAndStringify("if (x == (char *) ((void *)0)) ;", true)); } void removeCast7() { ASSERT_EQUALS("str = malloc ( 3 )", tokenizeAndStringify("str=(char **)malloc(3)", true)); } void removeCast8() { ASSERT_EQUALS("ptr1 = ptr2", tokenizeAndStringify("ptr1=(int * **)ptr2", true)); } void removeCast9() { 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 removeCast10() { ASSERT_EQUALS("; ( * f ) ( p ) ;", tokenizeAndStringify("; (*(void (*)(char *))f)(p);", true)); } void removeCast11() { ASSERT_EQUALS("; x = 0 ;", tokenizeAndStringify("; *(int *)&x = 0;", true)); } void removeCast12() { // #3935 - don't remove this cast ASSERT_EQUALS("; ( ( short * ) data ) [ 5 ] = 0 ;", tokenizeAndStringify("; ((short*)data)[5] = 0;", true)); } void removeCast13() { // 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 TODO_ASSERT_EQUALS("; float angle ; angle = - tilt ;", "; float angle ; angle = ( float ) - tilt ;", tokenizeAndStringify("; float angle = (float) -tilt;", true)); TODO_ASSERT_EQUALS("; float angle ; angle = tilt ;", "; float angle ; angle = ( float ) + tilt ;", tokenizeAndStringify("; float angle = (float) +tilt;", true)); } void removeCast14() { // 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 removeCast15() { // #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 simplifyFloatCasts() { // float casting integers // C-style casts ASSERT_EQUALS("a = 1.0f ;", tokenizeAndStringify("a = (float)1;")); ASSERT_EQUALS("a = 1.0f ;", tokenizeAndStringify("a = ((float)1);")); ASSERT_EQUALS("a = 291.0f ;", tokenizeAndStringify("a = ((float)0x123);")); ASSERT_EQUALS("a = 1.0 ;", tokenizeAndStringify("a = (double)1;")); ASSERT_EQUALS("a = 1.0 ;", tokenizeAndStringify("a = ((double)1);")); ASSERT_EQUALS("a = 291.0 ;", tokenizeAndStringify("a = ((double)0x123);")); ASSERT_EQUALS("a = 1.0 ;", tokenizeAndStringify("a = (long double)1;")); ASSERT_EQUALS("a = 1.0 ;", tokenizeAndStringify("a = ((long double)1);")); ASSERT_EQUALS("a = 291.0 ;", tokenizeAndStringify("a = ((long double)0x123);")); } 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\" );")); // 'asm ( ) ;' should be in the same line ASSERT_EQUALS(";\n\nasm ( \"\"mov ax,bx\"\" ) ;", tokenizeAndStringify(";\n\n__asm__ volatile ( \"mov ax,bx\" );", true)); } void pointers_condition() { ASSERT_EQUALS("( p )", tokenizeAndStringify("( p != NULL )", true)); ASSERT_EQUALS("( p )", tokenizeAndStringify("( NULL != p )", true)); ASSERT_EQUALS("( this . p )", tokenizeAndStringify("( this->p != NULL )", true)); ASSERT_EQUALS("( this . p )", tokenizeAndStringify("( NULL != this->p )", true)); ASSERT_EQUALS("( Foo :: p )", tokenizeAndStringify("( Foo::p != NULL )", true)); ASSERT_EQUALS("( Foo :: p )", tokenizeAndStringify("( NULL != Foo::p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == NULL )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( NULL == p )", true)); ASSERT_EQUALS("( ! this . p )", tokenizeAndStringify("( this->p == NULL )", true)); ASSERT_EQUALS("( ! this . p )", tokenizeAndStringify("( NULL == this->p )", true)); ASSERT_EQUALS("( ! Foo :: p )", tokenizeAndStringify("( Foo::p == NULL )", true)); ASSERT_EQUALS("( ! Foo :: p )", tokenizeAndStringify("( NULL == Foo::p )", true)); ASSERT_EQUALS("( p1 || ! p2 )", tokenizeAndStringify("( p1 != NULL || p2 == NULL )", true)); ASSERT_EQUALS("( p1 && ! p2 )", tokenizeAndStringify("( p1 != NULL && p2 == NULL )", true)); ASSERT_EQUALS("a & & b", tokenizeAndStringify("a & &b", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == false )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == 0 )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == '\\0' )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == 0L )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == 0UL )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == 0ul )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( p == 0l )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( false == p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( 0 == p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( '\\0' == p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( 0L == p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( 0UL == p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( 0ul == p )", true)); ASSERT_EQUALS("( ! p )", tokenizeAndStringify("( 0l == p )", true)); // not pointer ASSERT_EQUALS("( x != ( y != 0 ) )", tokenizeAndStringify("( x != ( y != 0 ) )", false)); } 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 ifAddBraces6() { const char code[] = "if()"; ASSERT_EQUALS("if ( )", 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)); } } 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(""); Settings settings; Tokenizer tokenizer(&settings, 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)); } 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 simplifyKnownVariables24() { { // This testcase is related to ticket #1596 const char code[] = "void foo()\n" "{\n" " int c;\n" " for (c=0;c<10;++c) { }\n" " a[c] = 0;\n" "}\n"; ASSERT_EQUALS( "void foo ( ) " "{" " int c ;" " for ( c = 0 ; c < 10 ; ++ c ) { }" " a [ 10 ] = 0 ; " "}", simplifyKnownVariables(code)); } { // #1692 - unknown counter value after for loop const char code[] = "void foo(const char s[])\n" "{\n" " int x[3];\n" " int i;\n" " for (i = 0; i < 3; ++i) {\n" " if (s[i]) break;\n" " }" " if (i < 3) x[i] = 0;\n" "}\n"; ASSERT_EQUALS( "void foo ( const char s [ ] ) " "{" " int x [ 3 ] ;" " int i ;" " for ( i = 0 ; i < 3 ; ++ i ) {" " if ( s [ i ] ) { break ; }" " }" " if ( i < 3 ) { x [ i ] = 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 ) { 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 simplifyKnownVariables26() { // This testcase is related to ticket #887 const char code[] = "void foo()\n" "{\n" " int i;\n" " for (i=0;i<10;++i) { }\n" " int k = i++;\n" "}\n"; ASSERT_EQUALS( "void foo ( ) " "{" " int i ;" " for ( i = 0 ; i < 10 ; ++ i ) { }" " int k ; k = 10 ; " "}", 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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "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"; const char current[] = "\n\n##file 0\n1: bool foo ( int u@1 , int v@2 )\n2: {\n3:\n4: int i@4 ; i@4 = v@2 ;\n5: return u@1 && i@4 ;\n6: }\n"; TODO_ASSERT_EQUALS(wanted, current, 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[] = "\n\n##file 0\n" "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"; const char current[] = "\n\n##file 0\n1: bool foo ( int u@1 , int v@2 )\n2: {\n3:\n4: int i@4 ; i@4 = v@2 ;\n5: return u@1 || i@4 ;\n6: }\n"; TODO_ASSERT_EQUALS(wanted, current, 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() {" " 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::Unspecified, "test.cpp")); } { const char expected[] = "void f ( ) {\n" "\n" "cin >> 0 ;\n" "return 0 ;\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Unspecified, "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::Unspecified, "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::Unspecified, "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::Unspecified, "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)); } } 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 = 9223372036854775808 ;", tokenizeAndStringify("unsigned long long x = 1UL << 63 ;", true)); ASSERT_EQUALS("long long x ; x = -9223372036854775808 ;", 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("int foo ( int ) ; " "void bar ( ) { throw foo ( 1 ) ; } " "int baz ( ) { return 2 ; }", tokenizeAndStringify(code, true)); } void simplifyKnownVariablesIfEq1() { const char code[] = "void f(int x) {\n" " if (x==5) {\n" " return x;\n" " }\n" "}"; const char expected[] = "void f ( int x ) {\n" "if ( x == 5 ) {\n" "return 5 ;\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Unspecified, "test.c")); } void simplifyKnownVariablesIfEq2() { const char code[] = "void f(int x) {\n" " if (x==5) {\n" " buf[x++] = 0;\n" " buf[x--] = 0;\n" " }\n" "}"; // Increment and decrements should be computed const char expected[] = "void f ( int x ) {\n" "if ( x == 5 ) {\n" "buf [ 5 ] = 0 ;\n" "buf [ 6 ] = 0 ;\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Unspecified, "test.c")); } void simplifyKnownVariablesIfEq3() { const char code[] = "void f(int x) {\n" " if (x==5) {\n" " buf[++x] = 0;\n" " buf[++x] = 0;\n" " buf[--x] = 0;\n" " }\n" "}"; const char expected[] = "void f ( int x ) {\n" "if ( x == 5 ) { " "x = 6 ;\n" "buf [ 6 ] = 0 ;\n" "buf [ 7 ] = 0 ;\n" "buf [ 6 ] = 0 ;\n" "}\n" "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(code, true, true, Settings::Unspecified, "test.c")); } 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 = 0 ;\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 simplifyKnownVariablesReturn() { const char code[] = "int a() {" " int x = 123;" " return (x);" "}"; ASSERT_EQUALS("int a ( ) { return 123 ; }", tokenizeAndStringify(code,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(); }")); } std::string tokenizeDebugListing(const char code[], bool simplify = false, const char filename[] = "test.cpp") { errout.str(""); Settings settings; settings.standards.c = Standards::C89; settings.standards.cpp = Standards::CPP03; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); if (simplify) tokenizer.simplifyTokenList2(); // result.. return tokenizer.tokens()->stringifyList(true); } void varid1() { { const std::string actual = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void f()\n" "{\n" " struct ABC abc;\n" " abc.a = 3;\n" " i = abc.a;\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "static char str[4];\n" "void f()\n" "{\n" " char str[10];\n" " str[0] = 0;\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void f(const unsigned int a[])\n" "{\n" " int i = *(a+10);\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "1: void f ( const 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 = tokenizeDebugListing( "void f()\n" "{\n" " int a,b;\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "int f(int a, int b)\n" "{\n" " return a+b;\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void func() {\n" " char a[256] = \"test\";\n" " {\n" " char b[256] = \"test\";\n" " }\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "int f()\n" "{\n" " int a;\n" " return a;\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void foo()\n" "{\n" " unsigned long mask = (1UL << size_) - 1;\n" " return (abits_val_ & mask);\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "1: void foo ( )\n" "2: {\n" "3: 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 = tokenizeDebugListing( "void func()\n" "{\n" " std::string str(\"test\");\n" " str.clear();\n" "}\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "typedef int INT32;\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "1: ;\n"); ASSERT_EQUALS(expected, actual); } void varid10() { const std::string actual = tokenizeDebugListing( "void foo()\n" "{\n" " int abc;\n" " struct abc abc1;\n" "}", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "class Foo;\n"); const std::string expected("\n\n##file 0\n" "1: class Foo ;\n"); ASSERT_EQUALS(expected, actual); } void varid12() { const std::string actual = tokenizeDebugListing( "static void a()\n" "{\n" " class Foo *foo;\n" "}\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void f()\n" "{\n" " int a; int b;\n" " a = a;\n" "}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void foo()\n" "{\n" "A a;\n" "B b;\n" "b * a;\n" "}", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "struct S {\n" " struct T {\n" " } t;\n" "} s;", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "struct S {\n" " struct T {\n" " } t;\n" "};", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(code, false, "test.c")); } void varid17() { // ticket #1810 const char code[] ="char foo()\n" "{\n" " char c('c');\n" " return c;\n" "}\n"; const std::string expected("\n\n##file 0\n" "1: char foo ( )\n" "2: {\n" "3: char c@1 ( 'c' ) ;\n" "4: return c@1 ;\n" "5: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code, false, "test.c")); } void varid18() { const char code[] ="char foo(char c)\n" "{\n" " bar::c = c;\n" "}\n"; const std::string expected("\n\n##file 0\n" "1: char foo ( char c@1 )\n" "2: {\n" "3: bar :: c = c@1 ;\n" "4: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid19() { const char code[] ="void foo()\n" "{\n" " std::pair, int> x;\n" "}\n"; const std::string expected("\n\n##file 0\n" "1: void foo ( )\n" "2: {\n" "3: std :: pair < std :: vector < double > , int > x@1 ;\n" "4: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid20() { const char code[] ="void foo()\n" "{\n" " pair, vector > x;\n" "}\n"; const std::string expected("\n\n##file 0\n" "1: void foo ( )\n" "2: {\n" "3: pair < vector < int > , vector < double > > x@1 ;\n" "4: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid24() { const char code[] ="class foo()\n" "{\n" "public:\n" " ;\n" "private:\n" " static int i;\n" "};\n"; const std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(code)); } void varid25() { const char code[] ="class foo()\n" "{\n" "public:\n" " ;\n" "private:\n" " mutable int i;\n" "};\n"; const std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(code)); } void varid26() { const char code[] ="list functions;\n"; const std::string expected("\n\n##file 0\n" "1: list < int ( * ) ( ) > functions@1 ;\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid27() { const char code[] ="int fooled_ya;\n" "fooled_ya::iterator iter;\n"; const std::string expected("\n\n##file 0\n" "1: int fooled_ya@1 ;\n" "2: fooled_ya :: iterator iter@2 ;\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid28() { // ticket #2630 (segmentation fault) tokenizeDebugListing("template \n"); ASSERT_EQUALS("", errout.str()); } void varid29() { const char code[] ="class A {\n" " B,1> b;\n" "};\n"; const std::string expected("\n\n##file 0\n" "1: class A {\n" "2: B < C < 1 > , 1 > b@1 ;\n" "3: } ;\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(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 std::string expected1("\n\n##file 0\n" "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, tokenizeDebugListing(code1, false, "test.c")); const char code2[] = "void f(int b, int c) {\n" " x(a*b*c,10);\n" "}\n"; const std::string expected2("\n\n##file 0\n" "1: void f ( int b@1 , int c@2 ) {\n" "2: x ( a * b@1 * c@2 , 10 ) ;\n" "3: }\n"); ASSERT_EQUALS(expected2, tokenizeDebugListing(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 std::string expected3("\n\n##file 0\n" "1: class Nullpointer : public ExecutionPath\n" "2: {\n" "3: Nullpointer ( Check * c@1 , const 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, tokenizeDebugListing(code3)); } void varid31() { // ticket #2831 (segmentation fault) const char code[] ="z"; tokenizeDebugListing(code); ASSERT_EQUALS("", errout.str()); } void varid32() { // ticket #2835 (segmentation fault) const char code[] ="><,f) const char code[] ="void f(std::vector)"; ASSERT_EQUALS("\n\n##file 0\n" "1: void f ( std :: vector < int > )\n", tokenizeDebugListing(code, false, "test.cpp")); } void varid50() { // #3760 - explicit const char code[] ="class A { explicit A(const A&); };"; ASSERT_EQUALS("\n\n##file 0\n" "1: class A { explicit A ( const A & ) ; } ;\n", tokenizeDebugListing(code, false, "test.cpp")); } void varid51() { // don't set varid on template function const char code[] ="T t; t.x<0>();"; ASSERT_EQUALS("\n\n##file 0\n" "1: T t@1 ; t@1 . x < 0 > ( ) ;\n", tokenizeDebugListing(code, false, "test.cpp")); } void varid52() { const char code[] ="A::D> e;\n" "B< C<> > b[10];\n" "B> c[10];"; ASSERT_EQUALS("\n\n##file 0\n" "1: A < B < C > :: D > e@1 ;\n" "2: B < C < > > b@2 [ 10 ] ;\n" "3: B < C < > > c@3 [ 10 ] ;\n", tokenizeDebugListing(code, false, "test.cpp")); } void varid53() { // #4172 - Template instantiation: T<&functionName> list[4]; ASSERT_EQUALS("\n\n##file 0\n" "1: A < & f > list@1 [ 4 ] ;\n", tokenizeDebugListing("A<&f> list[4];", false, "test.cpp")); } void varid54() { // hang // Original source code: libgc tokenizeDebugListing("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[] = "\n\n##file 0\n1: 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, tokenizeDebugListing(code, false, "test.cpp")); } void varid_cpp_keywords_in_c_code() { const char code[] = "void f() {\n" " delete d;\n" " throw t;\n" "}"; const char expected[] = "\n\n##file 0\n" "1: void f ( ) {\n" "2: delete d@1 ;\n" "3: throw t@2 ;\n" "4: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(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" "}"; tokenizeDebugListing(code, false, "test.c"); } void varidFunctionCall1() { const char code[] ="void f() {\n" " int x;\n" " x = a(y*x,10);\n" "}"; const std::string expected("\n\n##file 0\n" "1: void f ( ) {\n" "2: int x@1 ;\n" "3: x@1 = a ( y * x@1 , 10 ) ;\n" "4: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code, false, "test.c")); } void varidFunctionCall2() { // #2491 const char code[] ="void f(int b) {\n" " x(a*b,10);\n" "}"; const std::string expected1("\n\n##file 0\n" "1: void f ( int b@1 ) {\n" "2: x ( a * b"); const std::string expected2(" , 10 ) ;\n" "3: }\n"); ASSERT_EQUALS(expected1+"@1"+expected2, tokenizeDebugListing(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 std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(code)); } void varidFunctionCall4() { // Ticket #3280 const char code1[] = "void f() { int x; fun(a,b*x); }"; ASSERT_EQUALS("\n\n##file 0\n" "1: void f ( ) { int x@1 ; fun ( a , b * x@1 ) ; }\n", tokenizeDebugListing(code1, false, "test.c")); const char code2[] = "void f(int a) { int x; fun(a,b*x); }"; ASSERT_EQUALS("\n\n##file 0\n" "1: void f ( int a@1 ) { int x@2 ; fun ( a@1 , b * x@2 ) ; }\n", tokenizeDebugListing(code2, false, "test.c")); } void varidStl() { const std::string actual = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 :: tr1 :: 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 varid_delete() { const std::string actual = tokenizeDebugListing( "void f()\n" "{\n" " int *a;\n" " delete a;\n" "}\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "void f();\n" "void f(){}\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "1: void f ( ) ;\n" "2: void f ( ) { }\n"); ASSERT_EQUALS(expected, actual); } { const std::string actual = tokenizeDebugListing( "A f(3);\n" "A f2(true);\n" "A g();\n" "A e(int c);\n", false, "test.c"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing("void f(struct foobar);", false, "test.c"); const std::string expected("\n\n##file 0\n" "1: void f ( struct foobar ) ;\n"); ASSERT_EQUALS(expected, actual); } } void varid_sizeof() { const char code[] = "x = sizeof(a*b);"; const char expected[] = "\n\n##file 0\n" "1: x = sizeof ( a * b ) ;\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code,false,"test.c")); } void varid_reference_to_containers() { const std::string actual = tokenizeDebugListing( "void f()\n" "{\n" " std::vector b;\n" " std::vector &a = b;\n" " std::vector *c = &b;\n" "}\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "class Foo\n" "{\n" "public:\n" " std::string name1;\n" " std::string name2;\n" "};\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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("\n\n##file 0\n" "1: class Foo {\n" "2: void blah ( ) {\n" "3: Bar x@1 ( * this ) ;\n" "4: }\n" "5: int x@2 ;\n" "6: } ;\n", tokenizeDebugListing(code)); } void varid_in_class4() { const char code[] = "class Foo {\n" "public: class C;\n" "};"; ASSERT_EQUALS("\n\n##file 0\n" "1: class Foo {\n" "2: public: class C ;\n" "3: } ;\n", tokenizeDebugListing(code)); } void varid_in_class5() { const char code[] = "struct Foo {\n" " std::vector<::X> v;\n" "}"; ASSERT_EQUALS("\n\n##file 0\n" "1: struct Foo {\n" "2: std :: vector < :: X > v@1 ;\n" "3: }\n", tokenizeDebugListing(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("\n\n##file 0\n" "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", tokenizeDebugListing(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("\n\n##file 0\n" "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", tokenizeDebugListing(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("\n\n##file 0\n" "1: class A {\n" "2: UNKNOWN_MACRO ( A )\n" "3: private:\n" "4: int x@1 ;\n" "5: } ;\n", tokenizeDebugListing(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("\n\n##file 0\n" "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", tokenizeDebugListing(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("\n\n##file 0\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: g@3 . m_bar@4 = m_bar@2 ;\n" "7: }\n", tokenizeDebugListing(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("\n\n##file 0\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@4 = m_bar@2 ;\n" "7: }\n", "\n\n##file 0\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", tokenizeDebugListing(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("\n\n##file 0\n" "1: class Foo {\n" "2: union { float a@1 ; int b@2 ; } ;\n" "3: void f ( ) { a@1 = 0 ; }\n" "4: } ;\n", tokenizeDebugListing(code1)); const char code2[] = "class Foo {\n" " void f() { a=0; }\n" " union { float a; int b; };\n" "};"; ASSERT_EQUALS("\n\n##file 0\n" "1: class Foo {\n" "2: void f ( ) { a@1 = 0 ; }\n" "3: union { float a@1 ; int b@2 ; } ;\n" "4: } ;\n", tokenizeDebugListing(code2)); } void varid_in_class12() { // #4637 - method const char code[] = "class Foo {\n" "private:\n" " void f(void);\n" "};"; ASSERT_EQUALS("\n\n##file 0\n" "1: class Foo {\n" "2: private:\n" "3: void f ( ) ;\n" "4: } ;\n", tokenizeDebugListing(code)); } void varid_in_class13() { const char code1[] = "struct a { char typename; };"; ASSERT_EQUALS("\n\n##file 0\n" "1: struct a { char typename@1 ; } ;\n", tokenizeDebugListing(code1, false, "test.c")); ASSERT_EQUALS("\n\n##file 0\n" "1: struct a { char typename ; } ;\n", // not valid C++ code tokenizeDebugListing(code1, false, "test.cpp")); const char code2[] = "struct a { char typename[2]; };"; ASSERT_EQUALS("\n\n##file 0\n" "1: struct a { char typename@1 [ 2 ] ; } ;\n", tokenizeDebugListing(code2, false, "test.c")); ASSERT_EQUALS("\n\n##file 0\n" "1: struct a { char typename [ 2 ] ; } ;\n", // not valid C++ code tokenizeDebugListing(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("\n\n##file 0\n" "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", tokenizeDebugListing(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("\n\n##file 0\n" "1: class Fred {\n" "2: void x ( int a@1 ) const ;\n" "3: void y ( ) { a = 0 ; }\n" "4: }\n", tokenizeDebugListing(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("\n\n##file 0\n" "1: class Fred {\n" "2: int x@1 ;\n" "3: void foo ( int x@2 ) { this . x@1 = x@2 ; }\n" "4: }\n", tokenizeDebugListing(code, false, "test.cpp")); } void varid_initList() { const char code1[] = "class A {\n" " A() : x(0) {}\n" " int x;\n" "};"; ASSERT_EQUALS("\n\n##file 0\n" "1: class A {\n" "2: A ( ) : x@1 ( 0 ) { }\n" "3: int x@1 ;\n" "4: } ;\n", tokenizeDebugListing(code1)); const char code2[] = "class A {\n" " A(int x) : x(x) {}\n" " int x;\n" "};"; ASSERT_EQUALS("\n\n##file 0\n" "1: class A {\n" "2: A ( int x@1 ) : x@2 ( x@1 ) { }\n" "3: int x@2 ;\n" "4: } ;\n", tokenizeDebugListing(code2)); const char code3[] = "class A {\n" " A(int x);\n" " int x;\n" "};\n" "A::A(int x) : x(x) {}"; ASSERT_EQUALS("\n\n##file 0\n" "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", tokenizeDebugListing(code3)); const char code4[] = "struct A {\n" " int x;\n" " A(int x) : x(x) {}\n" "};\n"; ASSERT_EQUALS("\n\n##file 0\n" "1: struct A {\n" "2: int x@1 ;\n" "3: A ( int x@2 ) : x@1 ( x@2 ) { }\n" "4: } ;\n", tokenizeDebugListing(code4)); } void varid_operator() { { const std::string actual = tokenizeDebugListing( "class Foo\n" "{\n" "public:\n" " void operator=(const Foo &);\n" "};\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "struct Foo {\n" " void * operator new [](int);\n" "};\n"); const std::string expected("\n\n##file 0\n" "1: struct Foo {\n" "2: void * operatornew[] ( int ) ;\n" "3: } ;\n"); ASSERT_EQUALS(expected, actual); } } void varid_throw() { // ticket #1723 const std::string actual = tokenizeDebugListing( "UserDefinedException* pe = new UserDefinedException();\n" "throw pe;\n"); const std::string expected("\n\n##file 0\n" "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[] = "\n\n##file 0\n" "1: void f ( ) {\n" "2: int a@1 [ 10 ] ;\n" "3: AAA\n" "4: a@1 [ 0 ] = 0 ;\n" "5: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, false, "test.c")); } void varid_using() { // #3648 const char code[] = "using std::size_t;"; const char expected[] = "\n\n##file 0\n" "1: using long ;\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid_catch() { const char code[] = "void f() {\n" " try { dostuff(); }\n" " catch (exception &e) { }\n" "}"; const char expected[] = "\n\n##file 0\n" "1: void f ( ) {\n" "2: try { dostuff ( ) ; }\n" "3: catch ( exception & e@1 ) { }\n" "4: }\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varid_functionPrototypeTemplate() { ASSERT_EQUALS("\n\n##file 0\n" "1: function < void ( ) > fptr@1 ;\n", tokenizeDebugListing("function fptr;")); } void varid_templatePtr() { ASSERT_EQUALS("\n\n##file 0\n" "1: std :: map < int , FooTemplate < int > * > dummy_member@1 [ 1 ] ;\n", tokenizeDebugListing("std::map*> dummy_member[1];")); } void varid_templateNamespaceFuncPtr() { ASSERT_EQUALS("\n\n##file 0\n" "1: KeyListT < float , & NIFFile :: getFloat > mKeyList@1 [ 4 ] ;\n", tokenizeDebugListing("KeyListT mKeyList[4];")); } void varid_templateArray() { ASSERT_EQUALS("\n\n##file 0\n" "1: VertexArrayIterator < float [ 2 ] > attrPos@1 ; attrPos@1 = m_AttributePos . GetIterator < float [ 2 ] > ( ) ;\n", tokenizeDebugListing("VertexArrayIterator attrPos = m_AttributePos.GetIterator();")); } void varid_variadicFunc() { ASSERT_EQUALS("\n\n##file 0\n" "1: int foo ( . . . ) ;\n", tokenizeDebugListing("int foo(...);")); } void varid_typename() { ASSERT_EQUALS("\n\n##file 0\n" "1: template < int d , class A , class B >\n", tokenizeDebugListing("template")); ASSERT_EQUALS("\n\n##file 0\n" "1: template < int d , typename A , typename B >\n", tokenizeDebugListing("template")); ASSERT_EQUALS("\n\n##file 0\n" "1: typename A a@1 ;\n", tokenizeDebugListing("typename A a;")); } void varid_rvalueref() { ASSERT_EQUALS("\n\n##file 0\n" "1: int & & a@1 ;\n", tokenizeDebugListing("int&& a;")); ASSERT_EQUALS("\n\n##file 0\n" "1: void foo ( int & & a@1 ) { }\n", tokenizeDebugListing("void foo(int&& a) {}")); ASSERT_EQUALS("\n\n##file 0\n" "1: class C {\n" "2: C ( int & & a@1 ) ;\n" "3: } ;\n", tokenizeDebugListing("class C {\n" " C(int&& a);\n" "};")); ASSERT_EQUALS("\n\n##file 0\n" "1: void foo ( int & & ) ;\n", tokenizeDebugListing("void foo(int&&);")); } void varid_arrayFuncPar() { ASSERT_EQUALS("\n\n##file 0\n" "1: void check ( const char fname@1 [ ] = 0 ) { }\n", tokenizeDebugListing("void check( const char fname[] = 0) { }")); } void varid_sizeofPassed() { ASSERT_EQUALS("\n\n##file 0\n" "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", tokenizeDebugListing("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("\n\n##file 0\n" "1: void AddSuppression ( ) {\n" "2: class QErrorLogger {\n" "3: void reportErr ( ErrorLogger :: ErrorMessage & msg@1 ) {\n" "4: }\n" "5: } ;\n" "6: }\n", tokenizeDebugListing("void AddSuppression() {\n" " class QErrorLogger {\n" " void reportErr(ErrorLogger::ErrorMessage &msg) {\n" " }\n" " }; \n" "}")); } void varid_pointerToArray() { ASSERT_EQUALS("\n\n##file 0\n" "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", tokenizeDebugListing("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 varidclass1() { const std::string actual = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "class Fred\n" "{ int i; void f(); };\n" "\n" "void Fred::f()\n" "{\n" " if (i) { }\n" " i = 0;\n" "}\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "class A { };\n" "class B\n" "{\n" " A *a;\n" " B() : a(new A)\n" " { }\n" "};\n"); const std::string expected("\n\n##file 0\n" "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 = tokenizeDebugListing( "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 std::string wanted("\n\n##file 0\n" "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"); const char current[] = "\n\n##file 0\n1: class A\n2: {\n3: public:\n4: static char buf@1 [ 20 ] ;\n5: } ;\n6: char A :: buf [ 20 ] ;\n7: int main ( )\n8: {\n9: char buf@2 [ 2 ] ;\n10: A :: buf [ 10 ] = 0 ;\n11: }\n"; TODO_ASSERT_EQUALS(wanted, current, actual); } void varidclass7() { const std::string actual = tokenizeDebugListing( "int main()\n" "{\n" " char buf[2];\n" " A::buf[10] = 0;\n" "}"); const std::string expected("\n\n##file 0\n" "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 std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(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 std::string expected("\n\n" "##file 0\n" "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, tokenizeDebugListing(code)); } void varidclass10() { const char code[] ="class A {\n" " void f() {\n" " a = 3;\n" " }\n" " int a;\n" "};\n"; const std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(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 std::string expected("\n\n##file 0\n" "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, tokenizeDebugListing(code)); } void varidclass12() { const char code[] ="class Fred {\n" " int a;\n" " void f() { Fred::a = 0; }\n" "};\n"; const std::string expected("\n\n##file 0\n" "1: class Fred {\n" "2: int a@1 ;\n" "3: void f ( ) { Fred :: a@1 = 0 ; }\n" "4: } ;\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varidclass13() { const char code[] ="class Fred {\n" " int a;\n" " void f() { Foo::Fred::a = 0; }\n" "};\n"; const std::string expected("\n\n##file 0\n" "1: class Fred {\n" "2: int a@1 ;\n" "3: void f ( ) { Foo :: Fred :: a = 0 ; }\n" "4: } ;\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } void varidclass14() { // don't give friend classes varid { const char code[] ="class A {\n" "friend class B;\n" "}"; const std::string expected("\n\n##file 0\n" "1: class A {\n" "2: friend class B ;\n" "3: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(code)); } { const char code[] ="class A {\n" "private: friend class B;\n" "}"; const std::string expected("\n\n##file 0\n" "1: class A {\n" "2: private: friend class B ;\n" "3: }\n"); ASSERT_EQUALS(expected, tokenizeDebugListing(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[] = "\n\n##file 0\n" "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, tokenizeDebugListing(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[] = "\n\n##file 0\n" "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, tokenizeDebugListing(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[] = "\n\n##file 0\n" "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, tokenizeDebugListing(code)); } void file1() { const char code[] = "a1\n" "#file \"b\"\n" "b1\n" "b2\n" "#endfile\n" "a3\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "a"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { std::ostringstream ostr; ostr << char('a' + tok->fileIndex()) << tok->linenr(); ASSERT_EQUALS(tok->str(), ostr.str()); } } void file2() { const char code[] = "a1\n" "#file \"b\"\n" "b1\n" "b2\n" "#file \"c\"\n" "c1\n" "c2\n" "#endfile\n" "b4\n" "#endfile\n" "a3\n" "#file \"d\"\n" "d1\n" "#endfile\n" "a5\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "a"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { std::ostringstream ostr; ostr << char('a' + tok->fileIndex()) << tok->linenr(); ASSERT_EQUALS(tok->str(), ostr.str()); } } void file3() { const char code[] = "#file \"c:\\a.h\"\n" "123\n" "#endfile\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "a.cpp"); ASSERT_EQUALS(Path::toNativeSeparators("[c:\\a.h:1]"), tokenizer.list.fileLine(tokenizer.tokens())); } void line1() const { // Test for Ticket #4408 const char code[] = "#file \"c:\\a.h\"\n" "first\n" "#line 5\n" "second\n" "#line not-a-number\n" "third\n" "#line 100 \"i.h\"\n" "fourth\n" "fifth\n" "#endfile\n"; errout.str(""); Settings settings; TokenList tokenList(&settings); std::istringstream istr(code); bool res = tokenList.createTokens(istr, "a.cpp"); ASSERT_EQUALS(res, true); for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (tok->str() == "first") ASSERT_EQUALS(1, tok->linenr()); if (tok->str() == "second") ASSERT_EQUALS(5, tok->linenr()); if (tok->str() == "third") ASSERT_EQUALS(7, tok->linenr()); if (tok->str() == "fourth") ASSERT_EQUALS(100, tok->linenr()); if (tok->str() == "fifth") ASSERT_EQUALS(101, tok->linenr()); } } void line2() { const char code[] = "#line 8 \"c:\\a.h\"\n" "123\n"; errout.str(""); const Settings settings; // tokenize.. TokenList tokenlist(&settings); std::istringstream istr(code); tokenlist.createTokens(istr, "a.cpp"); ASSERT_EQUALS(Path::toNativeSeparators("[c:\\a.h:8]"), tokenlist.fileLine(tokenlist.front())); } void doublesharp() { const char code[] = "a##_##b TEST(var,val) var##_##val = val\n"; errout.str(""); Settings settings; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, ""); ASSERT_EQUALS("a_b TEST ( var , val ) var_val = val", tokenizer.tokens()->stringifyList(0, false)); } void macrodoublesharp() { const char code[] = "DBG(fmt,args...) printf(fmt, ## args)\n"; errout.str(""); Settings settings; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, ""); ASSERT_EQUALS("DBG ( fmt , args . . . ) printf ( fmt , ## args )", tokenizer.tokens()->stringifyList(0, false)); } 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 obsolete. 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 removeParentheses2() { const char code[] = "void foo()" "{" " if (__builtin_expect((s == NULL), 0))" " return;" "}"; ASSERT_EQUALS("void foo ( ) { if ( ! s ) { return ; } }", tokenizeAndStringify(code)); } 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 ) ) { ; } ;", 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 ; " "const static char * c ; " "} ;"; ASSERT_EQUALS(exp, tokenizeAndStringify(code)); } void tokenize_double() { const char code[] = "void f()\n" "{\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" "}\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS("void f ( ) { double a ; a = 4.2 ; float b ; b = 4.2f ; double c ; c = 4.2e+10 ; double d ; d = 4.2e-10 ; int e ; e = 6 ; }", tokenizer.tokens()->stringifyList(0, false)); } void tokenize_strings() { const char code[] = "void f()\n" "{\n" "const char *a =\n" "{\n" "\"hello \"\n" "\"more \"\n" "\"world\"\n" "};\n" "}\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("void f ( ) { const char * a ; a = { \"hello more world\" } ; }", tokenizer.tokens()->stringifyList(0, false)); } void simplify_constants() { const char code[] = "void f()\n" "{\n" "const int a = 45;\n" "if( a )\n" "{ int b = a; }\n" "}\n" "void g()\n" "{\n" "int a = 2;\n" "}\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("void f ( ) { } void g ( ) { }", tokenizer.tokens()->stringifyList(0, false)); } void simplify_constants2() { const char code[] = "void f( Foo &foo, Foo *foo2 )\n" "{\n" "const int a = 45;\n" "foo.a=a+a;\n" "foo2->a=a;\n" "}\n"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("void f ( Foo & foo , Foo * foo2 ) { foo . a = 90 ; foo2 . a = 45 ; }", tokenizer.tokens()->stringifyList(0, false)); } 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[] = "const static 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_null() { { const char code[] = "int * p = NULL;\n" "int * q = __null;\n"; const char expected[] = "int * p ; p = 0 ;\nint * q ; q = 0 ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(code,true)); } ASSERT_EQUALS("( a == nullptr )", tokenizeAndStringify("(a==nullptr)", false, false, Settings::Unspecified, "test.c")); ASSERT_EQUALS("( ! a )", tokenizeAndStringify("(a==nullptr)", false, false, Settings::Unspecified, "test.cpp")); } 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 = 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 = 0 ;"; ASSERT_EQUALS(res1, tokenizeAndStringify(code1)); const char code2[] = "const void *p = NULL;"; const char res2[] = "const void * p ; p = 0 ;"; ASSERT_EQUALS(res2, tokenizeAndStringify(code2)); const char code3[] = "void * const p = NULL;"; const char res3[] = "void * const p ; p = 0 ;"; ASSERT_EQUALS(res3, tokenizeAndStringify(code3)); const char code4[] = "const void * const p = NULL;"; const char res4[] = "const void * const p ; p = 0 ;"; ASSERT_EQUALS(res4, tokenizeAndStringify(code4)); } 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 = 0 ;", tokenizeAndStringify(code3)); } void vardecl_par() { // ticket #2743 - set links if variable type contains parentheses const char code[] = "Fred fred1=a, fred2=b;"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); tokenizer.validate(); } void vardecl_par2() { // ticket #3912 - set correct links const char code[] = "function)> v;"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp", ""); tokenizer.validate(); } 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 ; " "const static unsigned int A = 1 ; " "const static unsigned int B = A ; " "const static unsigned int C = 0 ; " "const static unsigned int D = A ; " "const static 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::Unspecified, "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" "a :: b const * 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" ":: a :: b const * p ; p = 0 ;\n" "}" , tokenizeAndStringify(code2)); } void vardecl22() { // #4211 - segmentation fault tokenizeAndStringify("A> >* p = 0;"); } void vardecl23() { // #4276 - segmentation fault tokenizeAndStringify("class a { protected : template < class int x = 1 ; public : int f ( ) ; }"); } 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::Unspecified, "test.c")); ASSERT_EQUALS(expected, tokenizeAndStringify(code)); } 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 syntax_error() { { errout.str(""); const char code[] = "void f() {}"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_EQUALS(true, tokenizer.tokenize(istr, "test.cpp")); ASSERT_EQUALS("", errout.str()); } { errout.str(""); const char code[] = "void f() {{}"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp"), InternalError); } { errout.str(""); const char code[] = "void f()) {}"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp"), InternalError); } { errout.str(""); const char code[] = "namespace extract{\nB(weighted_moment)\n}\nusing extract::weighted_moment;\n"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_EQUALS(true, tokenizer.tokenize(istr, "test.cpp")); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("", errout.str()); } { errout.str(""); const char code[] = "void f()\n" "{\n" " foo(;\n" "}\n"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp", "ABC"), InternalError); } { errout.str(""); const char code[] = "void f()\n" "{\n" " for(;;){ foo();\n" "}\n"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp"), InternalError); } { errout.str(""); const char code[] = "void f()\n" "{\n" " a[10;\n" "}\n"; Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp"), InternalError); } { errout.str(""); const char code[] = "{\n" " a(\n" "}\n" "{\n" " b());\n" "}\n"; Settings settings; 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 these macros are defined: ''.", e.errorMessage); ASSERT_EQUALS("syntaxError", e.id); ASSERT_EQUALS(2, e.token->linenr()); } } } void syntax_error_templates_1() { // ok code.. using ">" for a comparison { errout.str(""); std::istringstream istr("xz> xyz;\n"); Settings settings; Tokenizer tokenizer(&settings, this); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS("", errout.str()); } // ok code.. { errout.str(""); std::istringstream istr("template operator<(T a, T b) { }\n"); Settings settings; Tokenizer tokenizer(&settings, this); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS("", errout.str()); } // ok code (ticket #1984).. { errout.str(""); std::istringstream istr("void f(a) int a;\n" "{ ;x" { errout.str(""); std::istringstream istr("x xyz;\n"); Settings settings; Tokenizer tokenizer(&settings, this); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp"), InternalError); } // bad code { errout.str(""); std::istringstream istr("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"); Settings settings; Tokenizer tokenizer(&settings, this); ASSERT_THROW(tokenizer.tokenize(istr, "test.cpp"), InternalError); } // code is ok, don't show syntax error { errout.str(""); std::istringstream istr("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"); Settings settings; Tokenizer tokenizer(&settings, this); tokenizer.tokenize(istr, "test.cpp"); ASSERT_EQUALS("", errout.str()); } } void syntax_error_templates_2() { std::istringstream istr("template<>\n"); Settings settings; Tokenizer tokenizer(&settings, this); tokenizer.tokenize(istr, "test.cpp"); // shouldn't segfault } void syntax_error_templates_3() { // Ticket #5605, #5759, #5762, #5774, #5823 tokenizeAndStringify("foo() template struct tuple Args> tuple { } main() { foo(); }"); tokenizeAndStringify("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"); tokenizeAndStringify("() template < T = typename = x > struct a {} { f () }"); tokenizeAndStringify("template < T = typename = > struct a { f }"); tokenizeAndStringify("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>(); "); tokenizeAndStringify("template struct A {}; " "template <> struct A {}; " "void foo(const void* f = 0) {}"); tokenizeAndStringify("template struct A { " " static const int s = 0; " "}; " "A a;"); tokenizeAndStringify("template class A {}; " "template > class B {}; " "template > class C { " " C() : _a(0), _b(0) {} " " int _a, _b; " "};"); } void template_member_ptr() { // Ticket #5786 tokenizeAndStringify("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tokenizeAndStringify("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tokenizeAndStringify("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); tokenizeAndStringify("struct A {}; " "struct B { " "template struct BB {}; " "template static bool foo(int) { return true; } " "void bar() { bool b = foo(0); }" "};"); } void removeKeywords() { const char code[] = "if (__builtin_expect(!!(x), 1));"; const std::string actual(tokenizeAndStringify(code, true)); ASSERT_EQUALS("if ( ! ! x ) { ; }", actual); } void simplifyKeyword() { const char code[] = "void f (int a [ static 5] );"; const std::string actual(tokenizeAndStringify(code, true)); ASSERT_EQUALS("void f ( int a [ 5 ] ) ;", actual); } /** * 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)); } } void createLinks() { { const char code[] = "class A{\n" " void f() {}\n" "};"; errout.str(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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(""); Settings settings; Tokenizer tokenizer(&settings, 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()); } } void simplifyString() { errout.str(""); Settings settings; Tokenizer tokenizer(&settings, 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 ( ) { 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 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::Unspecified, "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::Unspecified, "test.c")); // C++: No pointer simplification ASSERT_EQUALS("foo data [ 100 ] ; something ( & foo [ 0 ] ) ;", tokenizeAndStringify("foo data[100]; something(&foo[0]);")); } std::string simplifyFunctionPointers(const char code[]) { errout.str(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyFunctionPointers(); return tokenizer.tokens()->stringifyList(0, true); } void functionpointer1() { ASSERT_EQUALS("void * f ;", simplifyFunctionPointers("void (*f)();")); ASSERT_EQUALS("void * * f ;", simplifyFunctionPointers("void *(*f)();")); ASSERT_EQUALS("unsigned int * f ;", simplifyFunctionPointers("unsigned int (*f)();")); ASSERT_EQUALS("unsigned int * * f ;", simplifyFunctionPointers("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, simplifyFunctionPointers(code)); } void functionpointer3() { // Related with ticket #2873 const char code[] = "void f() {\n" "(void)(xy(*p)(0);)" "\n}"; const char expected[] = "void f ( ) { " "( void ) ( xy ( * p ) ( 0 ) ; ) " "}"; ASSERT_EQUALS(expected, simplifyFunctionPointers(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[] = "\n\n##file 0\n" "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[] = "\n\n##file 0\n" "1: ; void * fp@1 [ ] = { 0 , 0 , 0 } ;\n"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, false)); } void functionpointer6() { const char code1[] = ";void (*fp(f))(int);"; const char expected1[] = "\n\n##file 0\n" "1: ; void * fp@1 ( f ) ;\n"; ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); const char code2[] = ";std::string (*fp(f))(int);"; const char expected2[] = "\n\n##file 0\n" "1: ; std :: string * fp@1 ( f ) ;\n"; ASSERT_EQUALS(expected2, tokenizeDebugListing(code2, false)); } void functionpointer7() { const char code1[] = "void (X::*y)();"; const char expected1[] = "\n\n##file 0\n" "1: void * y@1 ;\n"; ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, 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("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("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;")); } 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();"; const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ;"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, 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"); 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()); } 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__));"; const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ;"; errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, 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"); 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()); } 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)); } std::string arraySize_(const std::string &code) { errout.str(""); Settings settings; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); std::ostringstream ostr; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->isName() && tok->previous()) ostr << " "; ostr << tok->str(); } return ostr.str(); } void arraySize() { ASSERT_EQUALS("; int a[3]={1,2,3};", arraySize_(";int a[]={1,2,3};")); ASSERT_EQUALS("; int a[3]={1,2,3};", arraySize_(";int a[]={1,2,3,};")); ASSERT_EQUALS("; foo a[3]={{1,2},{3,4},{5,6}};", arraySize_(";foo a[]={{1,2},{3,4},{5,6}};")); TODO_ASSERT_EQUALS("; int a[1]={ foo< bar1, bar2>(123,4)};", "; int a[]={ foo< bar1, bar2>(123,4)};", arraySize_(";int a[]={foo(123,4)};")); ASSERT_EQUALS("; int a[2]={ b> c?1:2,3};", arraySize_(";int a[]={ b>c?1:2,3};")); TODO_ASSERT_EQUALS("int main(){ int a[2]={ b< c?1:2,3}}", "int main(){ int a[]={ b< c?1:2,3}}", arraySize_("int main(){int a[]={bx)=0; }")); //with '(' parentheses ASSERT_EQUALS("void f(){ ab:;*(* b). x=0;}", labels_("void f() { ab: *(* b)->x=0; }")); ASSERT_EQUALS("void f(){ ab:;(** b). x=0;}", labels_("void f() { ab: (** b).x=0; }")); ASSERT_EQUALS("void f(){ ab:;&(* b. x)=0;}", labels_("void f() { ab: &(*b.x)=0; }")); //with '{' parentheses ASSERT_EQUALS("void f(){ ab:;{ b=0;}}", labels_("void f() { ab: {b=0;} }")); ASSERT_EQUALS("void f(){ ab:;{* b=0;}}", labels_("void f() { ab: { *b=0;} }")); ASSERT_EQUALS("void f(){ ab:;{& b=0;}}", labels_("void f() { ab: { &b=0;} }")); ASSERT_EQUALS("void f(){ ab:;{&(* b. x)=0;}}", labels_("void f() { ab: {&(*b.x)=0;} }")); //with unhandled MACRO() code ASSERT_EQUALS("void f(){ MACRO( ab: b=0;, foo)}", labels_("void f() { MACRO(ab: b=0;, foo)}")); ASSERT_EQUALS("void f(){ MACRO( bar, ab:{&(* b. x)=0;})}", labels_("void f() { MACRO(bar, ab: {&(*b.x)=0;})}")); //don't crash with garbage code ASSERT_THROW(labels_("switch(){case}"), InternalError); } 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()); } } 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 ; } ;", tokenizeAndStringify(code6,false)); const char code7[] = "struct A { __int16 x : 3; };"; ASSERT_EQUALS("struct A { short x ; } ;", tokenizeAndStringify(code7,false)); const char code8[] = "struct A { __int32 x : 3; };"; ASSERT_EQUALS("struct A { int x ; } ;", tokenizeAndStringify(code8,false)); const char code9[] = "struct A { __int64 x : 3; };"; ASSERT_EQUALS("struct A { long long x ; } ;", tokenizeAndStringify(code9,false)); 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 ; } ;", tokenizeAndStringify(code14,false)); const char code15[] = "struct A { unsigned __int16 x : 3; };"; ASSERT_EQUALS("struct A { unsigned short x ; } ;", tokenizeAndStringify(code15,false)); const char code16[] = "struct A { unsigned __int32 x : 3; };"; ASSERT_EQUALS("struct A { unsigned int x ; } ;", tokenizeAndStringify(code16,false)); const char code17[] = "struct A { unsigned __int64 x : 3; };"; ASSERT_EQUALS("struct A { unsigned long long x ; } ;", tokenizeAndStringify(code17,false)); 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 ; } ;", tokenizeAndStringify(code21,false)); const char code22[] = "struct A { signed __int8 x : 3; };"; ASSERT_EQUALS("struct A { signed char x ; } ;", tokenizeAndStringify(code22,false)); const char code23[] = "struct A { signed __int16 x : 3; };"; ASSERT_EQUALS("struct A { signed short x ; } ;", tokenizeAndStringify(code23,false)); const char code24[] = "struct A { signed __int32 x : 3; };"; ASSERT_EQUALS("struct A { signed int x ; } ;", tokenizeAndStringify(code24,false)); const char code25[] = "struct A { signed __int64 x : 3; };"; ASSERT_EQUALS("struct A { signed long long x ; } ;", tokenizeAndStringify(code25,false)); } 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 bitfields11() { // ticket #2845 (segmentation fault) const char code[] = "#if b&&a\n" "#ifdef y z:\n"; tokenizeAndStringify(code,false); ASSERT_EQUALS("", errout.str()); } 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 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::Unspecified, "test.cpp", false)); ASSERT_EQUALS("std :: function < void ( int ) > f ;", tokenizeAndStringify(code9, false, true, Settings::Unspecified, "test.cpp", true)); static const char code10[] = "std::tr1::function f;"; ASSERT_EQUALS("std :: tr1 :: function < void ( int ) > f ;", tokenizeAndStringify(code10, false, true, Settings::Unspecified, "test.cpp", false)); ASSERT_EQUALS("std :: function < void ( int ) > f ;", tokenizeAndStringify(code10, false, true, Settings::Unspecified, "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 ( ) THROW_MACRO { }\n" "} ;"; ASSERT_EQUALS(expected14, tokenizeAndStringify(code14, false)); } void microsoftMFC() { const char code1[] = "class MyDialog : public CDialog { DECLARE_MESSAGE_MAP() private: CString text; };"; ASSERT_EQUALS("class MyDialog : public CDialog { private: CString text ; } ;", tokenizeAndStringify(code1,false,true,Settings::Win32A)); const char code2[] = "class MyDialog : public CDialog { DECLARE_DYNAMIC(MyDialog) private: CString text; };"; ASSERT_EQUALS("class MyDialog : public CDialog { private: CString text ; } ;", tokenizeAndStringify(code2,false,true,Settings::Win32A)); const char code3[] = "class MyDialog : public CDialog { DECLARE_DYNCREATE(MyDialog) private: CString text; };"; ASSERT_EQUALS("class MyDialog : public CDialog { private: CString text ; } ;", tokenizeAndStringify(code3,false,true,Settings::Win32A)); const char code4[] = "class MyDialog : public CDialog { DECLARE_DYNAMIC_CLASS(MyDialog) private: CString text; };"; ASSERT_EQUALS("class MyDialog : public CDialog { private: CString text ; } ;", tokenizeAndStringify(code4,false,true,Settings::Win32A)); } 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)); // __property ASSERT_EQUALS("class Fred { ; __property ; } ;", tokenizeAndStringify("class Fred { __property int x = { } };", false)); } 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()); } 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 ( \"\"EXEC SQL SELECT A FROM B\"\" ) ;", tokenizeAndStringify("EXEC SQL SELECT A FROM B;",false)); ASSERT_EQUALS("asm ( \"\"EXEC SQL\"\" ) ;", tokenizeAndStringify("EXEC SQL",false)); } void simplifyCAlternativeTokens() { ASSERT_EQUALS("void f ( ) { if ( a && b ) { ; } }", tokenizeAndStringify("void f() { if (a and b); }")); ASSERT_EQUALS("void f ( ) { if ( a || b ) { ; } }", tokenizeAndStringify("void f() { if (a or b); }")); ASSERT_EQUALS("void f ( ) { if ( a & b ) { ; } }", tokenizeAndStringify("void f() { if (a bitand b); }")); ASSERT_EQUALS("void f ( ) { if ( a | b ) { ; } }", tokenizeAndStringify("void f() { if (a bitor b); }")); ASSERT_EQUALS("void f ( ) { if ( a ^ b ) { ; } }", tokenizeAndStringify("void f() { if (a xor b); }")); ASSERT_EQUALS("void f ( ) { if ( ~ b ) { ; } }", tokenizeAndStringify("void f() { if (compl b); }")); ASSERT_EQUALS("void f ( ) { if ( ! b ) { ; } }", tokenizeAndStringify("void f() { if (not b); }")); ASSERT_EQUALS("void f ( ) { if ( a != b ) { ; } }", tokenizeAndStringify("void f() { if (a not_eq b); }")); } 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 ( 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 #3512 - Don't crash on garbage code ASSERT_EQUALS("p = const", tokenizeAndStringify("1 *p = const", 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)); // ticket #3964 - simplify numeric calculations in tokenization ASSERT_EQUALS("char a [ 10 ] ;", tokenizeAndStringify("char a[9+1];")); // #3953 (valgrind errors on garbage code) ASSERT_EQUALS("void f ( 0 * ) ;", tokenizeAndStringify("void f ( 0 * ) ;")); } void simplifyCompoundAssignment() { ASSERT_EQUALS("; x = x + y ;", tokenizeAndStringify("; x += y;")); ASSERT_EQUALS("; x = x - y ;", tokenizeAndStringify("; x -= y;")); ASSERT_EQUALS("; x = x * y ;", tokenizeAndStringify("; x *= y;")); ASSERT_EQUALS("; x = x / y ;", tokenizeAndStringify("; x /= y;")); ASSERT_EQUALS("; x = x % y ;", tokenizeAndStringify("; x %= y;")); ASSERT_EQUALS("; x = x & y ;", tokenizeAndStringify("; x &= y;")); ASSERT_EQUALS("; x = x | y ;", tokenizeAndStringify("; x |= y;")); ASSERT_EQUALS("; x = x ^ y ;", tokenizeAndStringify("; x ^= y;")); ASSERT_EQUALS("; x = x << y ;", tokenizeAndStringify("; x <<= y;")); ASSERT_EQUALS("; x = x >> y ;", tokenizeAndStringify("; x >>= y;")); ASSERT_EQUALS("{ x = x + y ; }", tokenizeAndStringify("{ x += y;}")); ASSERT_EQUALS("{ x = x - y ; }", tokenizeAndStringify("{ x -= y;}")); ASSERT_EQUALS("{ x = x * y ; }", tokenizeAndStringify("{ x *= y;}")); ASSERT_EQUALS("{ x = x / y ; }", tokenizeAndStringify("{ x /= y;}")); ASSERT_EQUALS("{ x = x % y ; }", tokenizeAndStringify("{ x %= y;}")); ASSERT_EQUALS("{ x = x & y ; }", tokenizeAndStringify("{ x &= y;}")); ASSERT_EQUALS("{ x = x | y ; }", tokenizeAndStringify("{ x |= y;}")); ASSERT_EQUALS("{ x = x ^ y ; }", tokenizeAndStringify("{ x ^= y;}")); ASSERT_EQUALS("{ x = x << y ; }", tokenizeAndStringify("{ x <<= y;}")); ASSERT_EQUALS("{ x = x >> y ; }", tokenizeAndStringify("{ x >>= y;}")); ASSERT_EQUALS("; * p = * p + y ;", tokenizeAndStringify("; *p += y;")); ASSERT_EQUALS("; ( * p ) = ( * p ) + y ;", tokenizeAndStringify("; (*p) += y;")); ASSERT_EQUALS("; * ( p [ 0 ] ) = * ( p [ 0 ] ) + y ;", tokenizeAndStringify("; *(p[0]) += y;")); ASSERT_EQUALS("void foo ( ) { switch ( n ) { case 0 : ; x = x + y ; break ; } }", tokenizeAndStringify("void foo() { switch (n) { case 0: x += y; break; } }")); ASSERT_EQUALS("; x . y = x . y + 1 ;", tokenizeAndStringify("; x.y += 1;")); ASSERT_EQUALS("; x [ 0 ] = x [ 0 ] + 1 ;", tokenizeAndStringify("; x[0] += 1;")); ASSERT_EQUALS("; x [ y - 1 ] = x [ y - 1 ] + 1 ;", tokenizeAndStringify("; x[y-1] += 1;")); ASSERT_EQUALS("; x [ y ] = x [ y ++ ] + 1 ;", tokenizeAndStringify("; x[y++] += 1;")); ASSERT_EQUALS("; x [ ++ y ] = x [ y ] + 1 ;", tokenizeAndStringify("; x[++y] += 1;")); ASSERT_EQUALS(";", tokenizeAndStringify(";x += 0;")); ASSERT_EQUALS(";", tokenizeAndStringify(";x += '\\0';")); ASSERT_EQUALS(";", tokenizeAndStringify(";x -= 0;")); ASSERT_EQUALS(";", tokenizeAndStringify(";x |= 0;")); ASSERT_EQUALS(";", tokenizeAndStringify(";x *= 1;")); ASSERT_EQUALS(";", tokenizeAndStringify(";x /= 1;")); ASSERT_EQUALS("; a . x ( ) = a . x ( ) + 1 ;", tokenizeAndStringify("; a.x() += 1;")); ASSERT_EQUALS("; x ( 1 ) = x ( 1 ) + 1 ;", tokenizeAndStringify("; x(1) += 1;")); // #2368 ASSERT_EQUALS("if ( false ) { } else { j = j - i ; }", tokenizeAndStringify("if (false) {} else { j -= i; }")); // #2714 - wrong simplification of "a += b?c:d;" ASSERT_EQUALS("; a = a + ( b ? c : d ) ;", tokenizeAndStringify("; a+=b?c:d;")); ASSERT_EQUALS("; a = a * ( b + 1 ) ;", tokenizeAndStringify("; a*=b+1;")); ASSERT_EQUALS("; a = a + ( b && c ) ;", tokenizeAndStringify("; a+=b&&c;")); ASSERT_EQUALS("; a = a * ( b || c ) ;", tokenizeAndStringify("; a*=b||c;")); ASSERT_EQUALS("; a = a | ( b == c ) ;", tokenizeAndStringify("; a|=b==c;")); // #3469 ASSERT_EQUALS("; a = a + ( b = 1 ) ;", tokenizeAndStringify("; a += b = 1;")); } void simplifyAssignmentInFunctionCall() { ASSERT_EQUALS("; x = g ( ) ; f ( x ) ;", tokenizeAndStringify(";f(x=g());")); } 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,false)); } 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 ;"; 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)); } void simplifyOperatorName9() { // Ticket #5709 const char code[] = "struct R { R operator, ( R b ) ; } ;"; ASSERT_EQUALS(code, tokenizeAndStringify(code)); } void simplifyNull() { ASSERT_EQUALS("if ( ! p )", tokenizeAndStringify("if (p==NULL)")); ASSERT_EQUALS("f ( NULL ) ;", tokenizeAndStringify("f(NULL);")); } 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 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 = tokenizeAndStringify(code, true, true, Settings::Win32A); ASSERT_EQUALS(expected, win32A); ASSERT_EQUALS(win32A, tokenizeAndStringify(code, true, true, Settings::Win32W)); ASSERT_EQUALS(win32A, tokenizeAndStringify(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 = tokenizeAndStringify(code, true, true, Settings::Win32A); ASSERT_EQUALS(expected, win32A); ASSERT_EQUALS(win32A, tokenizeAndStringify(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!\n\"));" " _stprintf(dst, _T(\"Hello!\n\"));" " _sntprintf(dst, sizeof(dst) / sizeof(TCHAR), _T(\"Hello world!\n\"));" " _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!\n\" ) ; " "sprintf ( dst , \"Hello!\n\" ) ; " "_snprintf ( dst , sizeof ( dst ) / sizeof ( char ) , \"Hello world!\n\" ) ; " "scanf ( \"%s\" , dst ) ; " "sscanf ( dst , \"%s\" , dst ) ; " "} " "unsigned short tbyte ;"; ASSERT_EQUALS(expected, tokenizeAndStringify(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!\n\"));" " _stprintf(dst, _T(\"Hello!\n\"));" " _sntprintf(dst, sizeof(dst) / sizeof(TCHAR), _T(\"Hello world!\n\"));" " _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 char 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!\n\" ) ; " "swprintf ( dst , L\"Hello!\n\" ) ; " "_snwprintf ( dst , sizeof ( dst ) / sizeof ( wchar_t ) , L\"Hello world!\n\" ) ; " "wscanf ( L\"%s\" , dst ) ; " "swscanf ( dst , L\"%s\" , dst ) ; " "}"; ASSERT_EQUALS(expected, tokenizeAndStringify(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, tokenizeAndStringify(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, tokenizeAndStringify(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, tokenizeAndStringify(code, true, true, Settings::Win32W)); } 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_fma() { // verify fma(), fmal(), fmaf() - simplifcation const char code_fma[] ="int f(int a, int b, int c) { return fma(a,b,c); }"; const char expected_fma[] = "int f ( int a , int b , int c ) { return ( a ) * ( b ) + ( c ) ; }"; ASSERT_EQUALS(expected_fma, tokenizeAndStringify(code_fma)); const char code_fmaf[] ="float f ( float a , float b , float c ) { return fmaf(a,b,c); }"; const char expected_fmaf[] = "float f ( float a , float b , float c ) { return ( a ) * ( b ) + ( c ) ; }"; ASSERT_EQUALS(expected_fmaf, tokenizeAndStringify(code_fmaf)); const char code_fmal[] ="long double f ( long double a , long double b , long double c ) { return fmal(a,b,c); }"; const char expected_fmal[] = "long double f ( long double a , long double b , long double c ) { return ( a ) * ( b ) + ( c ) ; }"; ASSERT_EQUALS(expected_fmal, tokenizeAndStringify(code_fmal)); const char code_fma1[] = "void f() {\n" " std::cout << \"fma(1,2,3): \" << fma(1,2,3) << std::endl;\n" " std::cout << \"fmaf(1,2,3): \" << fmaf(1,2,3) << std::endl;\n" " std::cout << \"fmal(1,2,3): \" << fmal(1,2,3) << std::endl;\n" "};"; const char expected_fma1[] = "void f() {\n" "std :: cout << \"fma(1,2,3): \" << 5 << std :: endl ;\n" "std :: cout << \"fmaf(1,2,3): \" << 5 << std :: endl ;\n" "std :: cout << \"fmal(1,2,3): \" << 5 << std :: endl ;\n" "} ;"; const char current_fma1[] = "void f ( ) {\n" "std :: cout << \"fma(1,2,3): \" << ( 1 ) * ( 2 ) + ( 3 ) << std :: endl ;\n" "std :: cout << \"fmaf(1,2,3): \" << ( 1 ) * ( 2 ) + ( 3 ) << std :: endl ;\n" "std :: cout << \"fmal(1,2,3): \" << ( 1 ) * ( 2 ) + ( 3 ) << std :: endl ;\n" "} ;"; TODO_ASSERT_EQUALS(expected_fma1, current_fma1,tokenizeAndStringify(code_fma1)); } 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)); } 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_div() { // verify div(), ldiv(), lldiv() - simplifcation const char code_div[] ="void f(int x) {\n" " std::cout << div(x,1);\n" //simplify " std::cout << div(x,-1);\n" // do not simplify " std::cout << ldiv(10L,1L);\n" // simplify " std::cout << ldiv(10L,132L);\n" // do not simplify " std::cout << lldiv(10LL,1LL);\n" // simplify " std::cout << lldiv(10LL,132LL);\n" // do not simplify "}"; const char expected_div[] = "void f ( int x ) {\n" "std :: cout << x ;\n" "std :: cout << div ( x , -1 ) ;\n" "std :: cout << 10L ;\n" "std :: cout << ldiv ( 10L , 132L ) ;\n" "std :: cout << 10LL ;\n" "std :: cout << lldiv ( 10LL , 132LL ) ;\n" "}"; ASSERT_EQUALS(expected_div, tokenizeAndStringify(code_div)); // Do not simplify class members. // case: div const char code_div1[] = "int f(const Fred &fred) {return fred.div(12,3);}"; const char expected_div1[] = "int f ( const Fred & fred ) { return fred . div ( 12 , 3 ) ; }"; ASSERT_EQUALS(expected_div1, tokenizeAndStringify(code_div1)); // case: ldiv const char code_div2[] = "int f(const Fred &fred) {return fred.ldiv(12,3);}"; const char expected_div2[] = "int f ( const Fred & fred ) { return fred . ldiv ( 12 , 3 ) ; }"; ASSERT_EQUALS(expected_div2, tokenizeAndStringify(code_div2)); // case: lldiv const char code_div3[] = "int f(const Fred &fred) {return fred.lldiv(12,3);}"; const char expected_div3[] = "int f ( const Fred & fred ) { return fred . lldiv ( 12 , 3 ) ; }"; ASSERT_EQUALS(expected_div3, tokenizeAndStringify(code_div3)); } 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_islessgreater() { // verify islessgreater() simplification const char code_islessgreater[] = "bool f(){\n" "return islessgreater(1,0);\n" // (1 < 0) or (1 > 0) --> true "}"; const char expected_islessgreater[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_islessgreater, tokenizeAndStringify(code_islessgreater)); const char code_islessgreater1[] = "bool f(){\n" "return islessgreater(0,1);\n" // (0 < 1) or (0 > 1) --> true "}"; const char expected_islessgreater1[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_islessgreater1, tokenizeAndStringify(code_islessgreater1)); const char code_islessgreater2[] = "bool f(){\n" "return islessgreater(0,0);\n" // (0 < 0) or (0 > 0) --> false "}"; const char expected_islessgreater2[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_islessgreater2, tokenizeAndStringify(code_islessgreater2)); const char code_islessgreater3[] = "bool f(int i){\n" "return islessgreater(i,0);\n" // <-- Do not simplify this "}"; const char expected_islessgreater3[] = "bool f ( int i ) {\nreturn islessgreater ( i , 0 ) ;\n}"; ASSERT_EQUALS(expected_islessgreater3, tokenizeAndStringify(code_islessgreater3)); } void simplifyMathFunctions_islessequal() { // verify islessequal() simplification const char code_islessequal[] = "bool f(){\n" "return islessequal(1,0);\n" // (1 <= 0) --> false "}"; const char expected_islessequal[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_islessequal, tokenizeAndStringify(code_islessequal)); const char code_islessequal1[] = "bool f(){\n" "return islessequal(0,1);\n" // (0 <= 1) --> true "}"; const char expected_islessequal1[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_islessequal1, tokenizeAndStringify(code_islessequal1)); const char code_islessequal2[] = "bool f(){\n" "return islessequal(0,0);\n" // (0 <= 0) --> true "}"; const char expected_islessequal2[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_islessequal2, tokenizeAndStringify(code_islessequal2)); const char code_islessequal3[] = "bool f(int i){\n" "return islessequal(i,0);\n" // <-- Do not simplify this "}"; const char expected_islessequal3[] = "bool f ( int i ) {\nreturn islessequal ( i , 0 ) ;\n}"; ASSERT_EQUALS(expected_islessequal3, tokenizeAndStringify(code_islessequal3)); } void simplifyMathFunctions_isless() { // verify isless() simplification const char code_isless[] = "bool f(){\n" "return isless(1,0);\n" // (1 < 0) --> false "}"; const char expected_isless[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_isless, tokenizeAndStringify(code_isless)); const char code_isless1[] = "bool f(){\n" "return isless(0,1);\n" // (0 < 1) --> true "}"; const char expected_isless1[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_isless1, tokenizeAndStringify(code_isless1)); const char code_isless2[] = "bool f(){\n" "return isless(0,0);\n" // (0 < 0) --> false "}"; const char expected_isless2[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_isless2, tokenizeAndStringify(code_isless2)); const char code_isless3[] = "bool f(int i){\n" "return isless(i,0);\n" // <-- Do not simplify this "}"; const char expected_isless3[] = "bool f ( int i ) {\nreturn isless ( i , 0 ) ;\n}"; ASSERT_EQUALS(expected_isless3, tokenizeAndStringify(code_isless3)); } void simplifyMathFunctions_isgreaterequal() { // verify isgreaterequal() simplification const char code_isgreaterequal[] = "bool f(){\n" "return isgreaterequal(1,0);\n" // (1 >= 0) --> true "}"; const char expected_isgreaterequal[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_isgreaterequal, tokenizeAndStringify(code_isgreaterequal)); const char code_isgreaterequal1[] = "bool f(){\n" "return isgreaterequal(0,1);\n" // (0 >= 1) --> false "}"; const char expected_isgreaterequal1[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_isgreaterequal1, tokenizeAndStringify(code_isgreaterequal1)); const char code_isgreaterequal2[] = "bool f(){\n" "return isgreaterequal(0,0);\n" // (0 >= 0) --> true "}"; const char expected_isgreaterequal2[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_isgreaterequal2, tokenizeAndStringify(code_isgreaterequal2)); const char code_isgreaterequal3[] = "bool f(int i){\n" "return isgreaterequal(i,0);\n" // <-- Do not simplify this "}"; const char expected_isgreaterequal3[] = "bool f ( int i ) {\nreturn isgreaterequal ( i , 0 ) ;\n}"; ASSERT_EQUALS(expected_isgreaterequal3, tokenizeAndStringify(code_isgreaterequal3)); } void simplifyMathFunctions_isgreater() { // verify isgreater() simplification const char code_isgreater[] = "bool f(){\n" "return isgreater(1,0);\n" // (1 > 0) --> true "}"; const char expected_isgreater[] = "bool f ( ) {\nreturn true ;\n}"; ASSERT_EQUALS(expected_isgreater, tokenizeAndStringify(code_isgreater)); const char code_isgreater1[] = "bool f(){\n" "return isgreater(0,1);\n" // (0 > 1) --> false "}"; const char expected_isgreater1[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_isgreater1, tokenizeAndStringify(code_isgreater1)); const char code_isgreater2[] = "bool f(){\n" "return isgreater(0,0);\n" // (0 > 0) --> false "}"; const char expected_isgreater2[] = "bool f ( ) {\nreturn false ;\n}"; ASSERT_EQUALS(expected_isgreater2, tokenizeAndStringify(code_isgreater2)); const char code_isgreater3[] = "bool f(int i){\n" "return isgreater(i,0);\n" // <-- Do not simplify this "}"; const char expected_isgreater3[] = "bool f ( int i ) {\nreturn isgreater ( i , 0 ) ;\n}"; ASSERT_EQUALS(expected_isgreater3, tokenizeAndStringify(code_isgreater3)); } 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 simplifyMathFunctions() { //#5031 // verify abs,fabs,labs,llabs,atol simplifcation const char code1[] = "void foo() {\n" " std::cout<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() const { // 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")); // 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;")); 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("absizeofd(ef.+(=", testAst("a = b(sizeof(c d) + e.f)")); ASSERT_EQUALS("a*b***", testAst("*a * **b;")); // Correctly distinguish between unary and binary operator* // for ASSERT_EQUALS("for;;(", testAst("for(;;)")); ASSERT_EQUALS("fora0=a8();")); 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);")); } void astpar() const { // 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;")); // ({..}) 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{{,( QT_WA{{,( x1=", testAst("QT_WA({},{x=0;});" // don't hang "QT_WA({x=1;},{x=2;});")); // 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("abuf0{={=", testAst("a = { .buf = { 0 } };")); ASSERT_EQUALS("tset{=", testAst("struct cgroup_taskset tset = {};")); } void astbrackets() const { // [] 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() const { // 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();")); // 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() const { 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 = (some)c;")); ASSERT_EQUALS("afoveon_avgimage((foveon_avgimage((+=", testAst("a = foveon_avg(((short(*)[4]) image)) + foveon_avg(((short(*)[4]) image));")); ASSERT_EQUALS("ab-(=", testAst("a = ((int)-b)")); // Multiple subsequent unary operators (cast and -) } void astlambda() const { ASSERT_EQUALS("([(return 0return", testAst("return [](){ return 0; }();")); ASSERT_EQUALS("([(return 0return", testAst("return []() -> 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);")); ASSERT_EQUALS("x([= 0return", testAst("x = [](){return 0; };")); } 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.. Settings settings; Preprocessor preprocessor(&settings); std::list configurations; std::string filedata = ""; std::istringstream fin(raw_code); preprocessor.preprocess(fin, filedata, configurations, "", settings._includePaths); const std::string code = preprocessor.getcode(filedata, "", ""); tokenizeAndStringify(code.c_str()); // just survive... } void astGarbage() { testAst("--"); // don't crash testAst("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] * {}"); // Don't hang (#5787) testAst("START_SECTION([EXTRA](bool isValid(const String &filename)))"); // Don't crash (#5991) } bool isStartOfExecutableScope(int offset, const char code[]) { const Settings settings; Tokenizer tokenizer(&settings, 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() 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) { }")); } }; REGISTER_TEST(TestTokenizer) cppcheck-1.66/test/testuninitvar.cpp000066400000000000000000004314111236713773000176450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkuninitvar.h" #include "testsuite.h" #include extern std::ostringstream errout; 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_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_strncpy); // strncpy doesn't always null-terminate TEST_CASE(uninitvar_memset); // not null-terminated TEST_CASE(uninitvar_memset_nonchar); TEST_CASE(uninitvar_memset_char_access); TEST_CASE(uninitvar_func); // analyse functions 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 // checking for uninitialized variables without using the // ExecutionPath functionality TEST_CASE(uninitvar2); TEST_CASE(uninitvar3); // #3844 TEST_CASE(uninitvar4); // #3869 (reference) TEST_CASE(uninitvar5); // #3861 TEST_CASE(uninitvar6); // handling unknown types in C and C++ files 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(uninitvar7); // ticket #5971 TEST_CASE(syntax_error); // Ticket #5073 // Test that std.cfg is configured correctly TEST_CASE(stdcfg); } void checkUninitVar(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); tokenizer.simplifyTokenList2(); // Check code.. CheckUninitVar check(&tokenizer, &settings, this); check.executionPaths(); } 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()); 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" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " int *x;\n" " int *&y = x;\n" "}"); 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" "}"); 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" "}"); 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"); TODO_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" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); } checkUninitVar("void a()\n" "{\n" " int x[10];\n" " int *y = x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void a()\n" "{\n" " int x;\n" " int *y = &x;\n" " *y = 0;\n" " x++;\n" "}"); 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" "}"); ASSERT_EQUALS("", errout.str()); // #5255 checkUninitVar("struct Element {\n" " static void abcd() {}\n" "};\n" "void a() {\n" " Element *e;\n" " e->abcd();\n" "}"); ASSERT_EQUALS("", errout.str()); // Handling >> and << { checkUninitVar("int a() {\n" " int ret;\n" " std::cin >> ret;\n" " ret++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int a() {\n" " int ret;\n" " int a = value >> ret;\n" " 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("int a() {\n" " int ret;\n" " int a = value << ret;\n" " return 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()); } 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 a()\n" "{\n" " struct S *s;\n" " FOREACH() { }\n" " s->x = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void a()\n" "{\n" " struct S *s1;\n" " struct S *s2;\n" " FOREACH(s1) { }\n" " s2->x = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: s2\n", errout.str()); // #1533 checkUninitVar("char a()\n" "{\n" " char key;\n" " struct A msg = { .buf = {&key} };\n" " init(&msg);\n" " key++;\n" "}"); ASSERT_EQUALS("", 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" " char *s = malloc(100);\n" " *s += 10;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: s\n", errout.str()); checkUninitVar("void f()\n" "{\n" " int a[10];\n" " a[0] = 10 - a[1];\n" "}"); 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" "}"); 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" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3480 - Don't crash garbage code ASSERT_THROW(checkUninitVar("int f()\n" "{\n" " return if\n" "}"), InternalError); // 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" "}"); 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()\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" "}"); 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" " 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("", 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" "{\n" " int x;\n" " x = v <= 0 ? -1 : x;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (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()); // while.. checkUninitVar("int f()\n" "{\n" " int i;\n" " while (fgets())\n" " i = 1;\n" " return i;" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("void f(int i)\n" "{\n" " int a;\n" " while (i < 10)\n" " i++;\n" " a++;" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: a\n", 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" "}"); TODO_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" "}"); 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" "}"); 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, sizeof(ret));\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" "\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()); // No segmentation fault checkUninitVar("void a() try\n" "{\n" " {\n" " while (1) {\n" " switch (1) {\n" " case 1: {\n" " int i;\n" " }\n" " }\n" " }\n" " } catch (...) {\n" " }\n" "}"); // #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" "}"); ASSERT_EQUALS("", errout.str()); } // arrays.. void uninitvar_arrays() { checkUninitVar("int f()\n" "{\n" " char a[10];\n" " a[a[0]] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("int f()\n" "{\n" " char a[10];\n" " char c = *a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar("int f()\n" "{\n" " char a[SIZE+10];\n" " char c = *a;\n" "}"); 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()); checkUninitVar("void f()\n" "{\n" " char c[50] = \"\";\n" " strcat(c, \"test\");\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " char s[20];\n" " strcpy(s2, s);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char s[20];\n" " strcat(s, \"abc\");\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char s[20];\n" " strchr(s, ' ');\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (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" "}"); 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" "}"); 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" "}"); 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" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", "", errout.str()); // # 4740 checkUninitVar("void f() {\n" " int *a[2][19];\n" " int **b = a[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } // alloc.. void uninitvar_alloc() { checkUninitVar("void f()\n" "{\n" " char *s = malloc(100);\n" " strcat(s, \"abc\");\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: s\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char *s = malloc(100);\n" " perror(s);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (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" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char *p = malloc(64);\n" " if (p[0]) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: p\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char *p = malloc(64);\n" " return p[0];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (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("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" "};"); 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()); } // 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" "}"); 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" "}"); 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" "}"); 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()); } void uninitvar_return() { checkUninitVar("static int foo()\n" "{\n" " int ret;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: ret\n", errout.str()); checkUninitVar("static int foo()\n" "{\n" " int ret;\n" " return ret+5;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (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 #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()); } // 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()); } // strncpy doesn't always null-terminate.. void uninitvar_strncpy() { checkUninitVar("void f()\n" "{\n" " char a[100];\n" " strncpy(a, s, 20);\n" " strncat(a, s, 20);\n" "}"); 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" "}"); 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" "}"); 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" "}"); ASSERT_EQUALS("", errout.str()); } // initialization with memset (not 0-terminating string).. void uninitvar_memset() { checkUninitVar("void f() {\n" " char a[20];\n" " memset(a, 'a', 20);\n" " strcat(a, s);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous usage of 'a' (not null-terminated).\n", errout.str()); } void uninitvar_memset_nonchar() { checkUninitVar("void f() {\n" " int a[20];\n" " memset(a, 1, 20);\n" " a[0] |= 2;\n" "}"); ASSERT_EQUALS(errout.str(), ""); } void uninitvar_memset_char_access() { checkUninitVar("void f() {\n" " unsigned char c[10];\n" " memset(c, 32, 10);\n" " unsigned char value = c[3];\n" "}"); ASSERT_EQUALS(errout.str(), ""); } std::string analyseFunctions(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); std::set f; const CheckUninitVar check((const Tokenizer *)0, (const Settings *)0, (ErrorLogger *)0); check.analyse(tokenizer.tokens(), f); std::string ret; for (std::set::const_iterator it = f.begin(); it != f.end(); ++it) ret += (ret.empty() ? "" : " ") + *it; return ret; } void uninitvar_func() { // function analysis.. ASSERT_EQUALS("foo", analyseFunctions("void foo(int x) { }")); ASSERT_EQUALS("foo", analyseFunctions("void foo(int x);")); ASSERT_EQUALS("foo", analyseFunctions("void foo(const int &x) { }")); ASSERT_EQUALS("foo", analyseFunctions("void foo(int &x) { ++x; }")); ASSERT_EQUALS("rename", analyseFunctions("int rename (const char* oldname, const char* newname);")); // Ticket #914 ASSERT_EQUALS("rename", analyseFunctions("int rename (const char oldname[], const char newname[]);")); ASSERT_EQUALS("", analyseFunctions("void foo(int &x) { x = 0; }")); ASSERT_EQUALS("", analyseFunctions("void foo(s x) { }")); // TODO: it's ok to pass a valid pointer to "foo". See #2775 and #2946 TODO_ASSERT_EQUALS("foo", "", analyseFunctions("void foo(Fred *fred) { fred->x = 0; }")); ASSERT_EQUALS("", analyseFunctions("void foo(int *x) { x[0] = 0; }")); // function calls.. checkUninitVar("void assignOne(int &x)\n" "{ x = 1; }\n" "\n" "int f()\n" "{\n" " int i;\n" " assignOne(i);\n" " return i;\n" "};"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f(int (*assign)(int *p))\n" "{\n" " int i;\n" " (*assign)(&i);\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int f()\n" "{\n" " char s[10];\n" " return bar(s);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " FILE *f;\n" " fflush(f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n", errout.str()); checkUninitVar("void f()\n" "{\n" " int i;\n" " x(i+2);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f()\n" "{\n" " int i;\n" " x(i+2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar("void f()\n" "{\n" " char *p = malloc(10);\n" " read(p + 1);\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f()\n" "{\n" " Abc *p;\n" " int sz = sizeof(*p);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " Foo *p;\n" " x = bar(sizeof(*p));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void foo()\n" "{\n" " Foo *p;\n" " x = bar(p->begin());\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("int foo(int x) { return x; }\n" "void f2()\n" "{\n" " int x;\n" " foo(x);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("int foo(int x) { return x; }\n" "void f2()\n" "{\n" " int x;\n" " foo(x);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar("void foo(const char *s)\n" "{\n" " char *p;\n" " memcpy(p, s, 100);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("void foo(const char *s)\n" "{\n" " char *p = malloc(100);\n" " memcpy(p, s, 100);\n" "}"); ASSERT_EQUALS("", errout.str()); 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()); // #2401 - unknown function/macro might init the variable checkUninitVar("int f() {\n" " int x;\n" " INIT(x);\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3222 - calling function through function pointer checkUninitVar("char f() {\n" " char buffer[100];\n" " (foo.init)(buffer);\n" " return buffer[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" // #3586 - calling template function " int i;\n" " a::b(i);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void test() {\n" " double d;\n" " double x = dostuff(d);\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("template double dostuff(int x, T &y);\n" "void test() {\n" " double d;\n" " a = dostuff(0, d);\n" "}"); ASSERT_EQUALS("", errout.str()); // using uninitialized function pointer.. checkUninitVar("void foo()\n" "{\n" " void (*f)();\n" " f();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n", errout.str()); // calling noreturn function.. checkUninitVar("int foo(int a) {\n" " int x;\n" " if (a==1)\n" " g();\n" // might be a noreturn function " else\n" " x = 3;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("int foo(int a) {\n" " int x;\n" " if (a==1)\n" " g(1);\n" // might be a noreturn function " else\n" " x = 3;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void (*init)(char *str);\n" "\n" "char x() {\n" " char cmd[10];\n" " init(cmd);\n" " return cmd[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("char fn(FILE *f) {\n" " char buf[10];\n" " fread(buf, 1, 10, f);\n" "}"); ASSERT_EQUALS("", 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" "}"); // TODO: See #2946 TODO_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()); // #3159 - initialization by function checkUninitVar("static int isnumber(const char *arg) {\n" " char *p;\n" " return strtod(arg, &p) != 0 || p != arg;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("static int isnumber(const char *arg) {\n" " char *p;\n" " return strtod(&arg) != 0 || p != arg;\n" "}"); TODO_ASSERT_EQUALS("error", "", 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("", errout.str()); checkUninitVar2((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("", errout.str()); checkUninitVar2((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" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f() {\n" " #define w(x) ({ x z; (x*)z; })\n" " int *n = w(typeof(*n));\n" "}"); ASSERT_EQUALS("", errout.str()); } /** New checking that doesn't rely on ExecutionPath */ void checkUninitVar2(const char code[], const char fname[] = "test.cpp", bool verify=true, bool debugwarnings=false) { // Clear the error buffer.. errout.str(""); // Tokenize.. Settings settings1(settings); settings1.debugwarnings = debugwarnings; Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, fname); const std::string str1(tokenizer.tokens()->stringifyList(0,true)); tokenizer.simplifyTokenList2(); const std::string str2(tokenizer.tokens()->stringifyList(0,true)); if (verify && str1 != str2) warn(("Unsimplified code in test case. It looks like this test " "should either be cleaned up or moved to TestTokenizer or " "TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str()); // Check for redundant code.. CheckUninitVar checkuninitvar(&tokenizer, &settings1, this); checkuninitvar.testrunner = true; checkuninitvar.check(); } void uninitvar2() { // using uninit var checkUninitVar2("void f() {\n" " int x;\n" " x++;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " str[x] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " int y = x & 3;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " int y = 3 & x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " x = 3 + x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("int f() {\n" " int x;\n" " x = x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("void f() {\n" " struct ABC *abc;\n" " abc->a = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: abc\n", errout.str()); checkUninitVar2("int f() {\n" " static int x;\n" " return ++x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("int f() {\n" " extern int x;\n" " return ++x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #3926 - weird cast. " int x;\n" " *(((char *)&x) + 0) = 0;\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #4737 - weird cast. " int x;\n" " do_something(&((char*)&x)[0], 1);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " char *p = (char*)&x + 1;\n" "}", "test.cpp", false); // verify=false (the cast is removed but we don't care) ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int i;\n" " i=f(), i!=2;\n" "}"); ASSERT_EQUALS("", errout.str()); // using uninit var in condition checkUninitVar2("void f(void) {\n" " int x;\n" " if (x) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " if (1 == (3 & x)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: x\n", errout.str()); // ?: checkUninitVar2("int f(int *ptr) {\n" " int a;\n" " int *p = ptr ? ptr : &a;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("int f(int a) {\n" " int x;\n" " if (a==3) { x=2; }\n" " y = (a==3) ? x : a;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("int f(int a) {\n" " int result;\n" " foo() ? result = 1 : result = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // = { .. } checkUninitVar2("int f() {\n" " int a;\n" " int *p[] = { &a };\n" " *p[0] = 0;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); // = ({ .. }) checkUninitVar2("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" "}"; checkUninitVar2(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); checkUninitVar2(code, "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Uninitialized variable: x\n", errout.str()); } checkUninitVar2("void f() {\n" " int i, i2;\n" " strm >> i >> i2;\n" "}"); ASSERT_EQUALS("", errout.str()); // unconditional initialization checkUninitVar2("int f() {\n" " int ret;\n" " if (a) { ret = 1; }\n" " else { {} ret = 2; }\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); // conditional initialization checkUninitVar2("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()); checkUninitVar2("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 checkUninitVar2("void f() {\n" " int a;\n" " if (init(&a)) { }\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); // return, break, continue, goto checkUninitVar2("void f() {\n" " int x;\n" " if (y == 1) { return; }\n" " else { x = 1; }\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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", false); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int i;\n" " if (x) {\n" " i = 1;\n" " } else {\n" " goto out;\n" " }\n" " i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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 checkUninitVar2("void f() {\n" " try {\n" " } catch (CException* e) {\n" " trace();\n" " e->Delete();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #5347 " try {\n" " } catch (const char* e) {\n" " A a = e;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // exit checkUninitVar2("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) checkUninitVar2("void foo() {\n" " int i;\n" " ({ if (0); });\n" " for_each(i) { }\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); // if, if checkUninitVar2("void f(int a) {\n" " int i;\n" " if (a) i = 0;\n" " if (a) i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("static void f(int x, int y) {\n" " int a;\n" " if (x == 0 && (a == 1)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); checkUninitVar2("void f() {\n" " int a;\n" " if (x) { a = 0; }\n" " if (x) { if (y) { a++; } }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int a;\n" " if (x) { a = 0; }\n" " if (x) { if (y) { } else { a++; } }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); // asm checkUninitVar2("void f() {\n" " int x;\n" " asm();\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); // sizeof / typeof / offsetof / etc checkUninitVar2("void f() {\n" " int i;\n" " sizeof(i+1);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int i;\n" " if (100 == sizeof(i+1));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " struct ABC *abc;\n" " int i = ARRAY_SIZE(abc.a);" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int *abc;\n" " typeof(*abc);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " struct ABC *abc;\n" " return do_something(typeof(*abc));\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " A *a;\n" " a = malloc(sizeof(*a));\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3486 - Don't crash garbage code checkUninitVar2("void f()\n" "{\n" " (\n" " x;\n" " int a, a2, a2*x; if () ;\n" " )\n" "}"); // Ticket #3890 - False positive for std::map checkUninitVar2("void f() {\n" " std::map x;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3906 - False positive for std::vector pointer checkUninitVar2("void f() {\n" " std::vector *x = NULL;\n" " return x;\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); // & checkUninitVar2("void f() {\n" // #4426 - address of uninitialized variable " int a,b;\n" " if (&a == &b);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #4439 - cast address of uninitialized variable " int a;\n" " x((A)(B)&a);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #4778 - cast address of uninitialized variable " long a;\n" " &a;\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #4717 - ({}) " int a = ({ long b = (long)(123); 2 + b; });\n" "}", "test.c", false); ASSERT_EQUALS("", errout.str()); } // #3869 - reference variable void uninitvar4() { checkUninitVar2("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 checkUninitVar2("void f() {\n" " x c;\n" " c << 2345;\n" "}"); ASSERT_EQUALS("", errout.str()); // ensure there is no false negative checkUninitVar2("void f() {\n" " char c;\n" " char a = c << 2;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: c\n", errout.str()); // #4320 checkUninitVar2("void f() {\n" " int a;\n" " a << 1;\n" // there might be a operator<< "}"); ASSERT_EQUALS("", errout.str()); } // Handling of unknown types. Assume they are POD in C. void uninitvar6() { const char code[] = "void f() {\n" " dfs a;\n" " return a;\n" "}"; // Assume dfs is a non POD type if file is C++ checkUninitVar2(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); // Assume dfs is a POD type if file is C checkUninitVar2(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" "}"; checkUninitVar2(code2, "test.cpp"); ASSERT_EQUALS("", errout.str()); checkUninitVar2(code2, "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Uninitialized variable: ab\n", errout.str()); } void uninitvar7() { const char code[] = "void eDBauth_user() {\n" " char *blid_cert;\n" " if( ) {\n" " blid_cert = ;\n" " } \n" "}\n"; // Assume dfs is a non POD type if file is C++ checkUninitVar2(code, "test.cpp"); } // Handling of function calls void uninitvar2_func() { // non-pointer variable checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("void a(char *c);\n" // address => no error "void b() {\n" " char c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void a(pstr s);\n" // address => no error "void b() {\n" " char c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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 checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("void a(char **c);\n" // address of pointer => no error "void b() {\n" " char *c;\n" " a(&c);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); } void uninitvar2_value() { checkUninitVar2("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" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("void f(int x) {\n" " int i;\n" " if (!x) i = 0;\n" " if (!x || i>0) {}\n" // <- error "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); checkUninitVar2("void f(int x) {\n" " int i;\n" " if (x) i = 0;\n" " if (!x || i>0) {}\n" // <- no error "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("int f(int x) {\n" // FP with ?: " int a;\n" " if (x)\n" " a = p;\n" " return x ? 2*a : 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // TODO: False negative when "?:" is used // This should probably be fixed in the tokenizer by changing // "return x?y:z;" to "if(x)return y;return z;" checkUninitVar2("int f(int x) {\n" " int a;\n" " if (x)\n" " a = p;\n" " return y ? 2*a : 3*a;\n" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); // Unknown => bail out.. checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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 checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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:6]: (error) Uninitialized struct member: abc.b\n", errout.str()); // return checkUninitVar2("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()); checkUninitVar2("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 checkUninitVar2("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()); checkUninitVar2("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"); ASSERT_EQUALS("[test.c:9]: (error) Uninitialized struct member: fred.b\n", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("void test2() {\n" " struct { char type; } s_d;\n" " if (foo(&s_d.type)){}\n" "}"); ASSERT_EQUALS("", errout.str()); // for checkUninitVar2("struct AB { int a; };\n" "void f() {\n" " struct AB ab;\n" " while (x) { clear(ab); z = ab.a; }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); // address of member checkUninitVar2("struct AB { int a[10]; int b; };\n" "void f() {\n" " struct AB ab;\n" " int *p = ab.a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar2_while() { // for, while checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("void f() {\n" " for (int x = x; x < 10; x++) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: x\n", errout.str()); checkUninitVar2("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()); checkUninitVar2("class Element {\n" " static void f() { }\n" "};\n" "void test() {\n" " Element *element; element->f();\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" // #4911 - bad simplification => don't crash " int a;\n" " do { a=do_something() } while (a);\n" "}\n", "test.cpp", /*verify=*/true, /*debugwarnings=*/true); ASSERT_EQUALS("[test.cpp:3]: (debug) ValueFlow bailout: variable a stopping on }\n", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " while (a) {\n" " init(&x);\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " while (a) {\n" " if (b) x++;\n" " else x = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " int x;\n" " for (int i = 0; i < 10; i += x) {\n" " x = y;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("void f() {\n" " int i;\n" " do {} while (!getvalue(&i));\n" " i++;\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2(">{ x while (y) z int = }"); // #4175 : don't crash checkUninitVar2("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" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); checkUninitVar2("int f(void) {\n" " int x;\n" " while (a()) {\n" " if (b() && (x=1)) {\n" " return x;\n" " }\n" " }\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("static void f(void) {\n" " struct ABC *abc;\n" " for (i = 0; i < 10; i++)\n" " x += sizeof(*abc);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("void f(void) {\n" " int i;\n" " while (x) {\n" " for (i=0,y=i;;){}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitvar2_4494() { checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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() { checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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) Uninitialized struct member: ab.a\n", errout.str()); checkUninitVar2("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" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " char *s = malloc(100);\n" " if (s != NULL) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " char *s = malloc(100);\n" " *s = x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " char *p = malloc(100);\n" " p || assert_failed();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkUninitVar2("void f() {\n" " char *p = malloc(100);\n" " x = p;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // function parameter (treat it as initialized until malloc is used) checkUninitVar2("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()); checkUninitVar2("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()); checkUninitVar2("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. checkUninitVar2("void *vlc_custom_create (vlc_object_t *parent, size_t length, const char *typename) {\n" " assert (length >= sizeof (vlc_object_t));\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void syntax_error() { // Ticket #5073 // Nominal mode => No output checkUninitVar2("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" "}", "test.cpp", /*verify=*/true, /*debugwarnings=*/false); ASSERT_EQUALS("", errout.str()); // --debug-warnings mode => Debug warning checkUninitVar2("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" "}", "test.cpp", /*verify=*/true, /*debugwarnings=*/true); ASSERT_EQUALS("[test.cpp:6]: (debug) assertion failed '} while ('\n", errout.str()); } // Test that std.cfg is configured correctly void stdcfg() { // clearerr checkUninitVar("void f() {\n" " FILE * pFile;\n" " clearerr (pFile);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: pFile\n", errout.str()); checkUninitVar("void f(FILE * pFile) {\n" " clearerr (pFile);\n" "}"); ASSERT_EQUALS("", errout.str()); // fclose checkUninitVar("void f() {\n" " FILE * pFile;\n" " fclose (pFile);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: pFile\n", errout.str()); checkUninitVar("void f(FILE * pFile) {\n" " fclose (pFile);\n" "}"); ASSERT_EQUALS("", errout.str()); // fopen checkUninitVar("void f() {\n" " char * filename;\n" " fopen (filename, \"w\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: filename\n", errout.str()); checkUninitVar("void f() {\n" " char * filename;\n" " char * mode;\n" " fopen (filename, mode);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: filename\n" "[test.cpp:4]: (error) Uninitialized variable: mode\n", errout.str()); checkUninitVar("void f(FILE * name, char *mode) {\n" " fopen (name, mode);\n" "}"); ASSERT_EQUALS("", errout.str()); // feof checkUninitVar("void f() {\n" " FILE * pFile;\n" " feof (pFile);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: pFile\n", errout.str()); checkUninitVar("void f(FILE * pFile) {\n" " feof (pFile);\n" "}"); ASSERT_EQUALS("", errout.str()); // ferror checkUninitVar("void f() {\n" " FILE * pFile;\n" " ferror (pFile);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: pFile\n", errout.str()); checkUninitVar("void f(FILE * pFile) {\n" " ferror (pFile);\n" "}"); ASSERT_EQUALS("", errout.str()); // fflush checkUninitVar("void f() {\n" " FILE * pFile;\n" " fflush (pFile);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: pFile\n", errout.str()); checkUninitVar("void f(FILE * pFile) {\n" " fflush (pFile);\n" "}"); ASSERT_EQUALS("", errout.str()); // fgetc checkUninitVar("void f() {\n" " FILE * pFile;\n" " fgetc (pFile);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: pFile\n", errout.str()); checkUninitVar("void f(FILE * pFile) {\n" " fgetc (pFile);\n" "}"); ASSERT_EQUALS("", errout.str()); // fgetpos checkUninitVar("void f() {\n" " FILE * f;\n" " fpos_t * p;\n" " fgetpos (f, p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n" "[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("void f(FILE * f) {\n" " fpos_t p;" " fgetpos (f, &p);\n" "}"); ASSERT_EQUALS("", errout.str()); checkUninitVar("void f(FILE * f, fpos_t *p) {\n" " fgetpos (f, p);\n" "}"); ASSERT_EQUALS("", errout.str()); // fsetpos checkUninitVar("void f() {\n" " FILE * f;\n" " fpos_t * p;\n" " fsetpos (f, p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n" "[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("void f(FILE * f) {\n" " fpos_t *p;" " fsetpos (f, p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: p\n", errout.str()); checkUninitVar("void f(FILE * f, fpos_t *p) {\n" " fsetpos (f, p);\n" "}"); ASSERT_EQUALS("", errout.str()); // fgets checkUninitVar("void f(FILE *f) {\n" " char *s;\n" " int n;\n" " fgets (s, n, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: n\n" "[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); checkUninitVar("void f(char * s, int n) {\n" " FILE *f;\n" " fgets (s, n, f);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: f\n","", errout.str()); checkUninitVar("void f(char * s, int n, FILE *f) {\n" " fgets (s, n, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // fputc checkUninitVar("void f() {\n" " int c;\n" " FILE *f;" " fputc (c, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: c\n" "[test.cpp:3]: (error) Uninitialized variable: f\n", errout.str()); checkUninitVar("void f(int c, FILE *f) {\n" " fputc (c, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // fputs checkUninitVar("void f() {\n" " char *c;\n" " FILE *f;" " fputs (c, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: c\n" "[test.cpp:3]: (error) Uninitialized variable: f\n", errout.str()); checkUninitVar("void f(char *c, FILE *f) {\n" " fputs (c, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // ftell checkUninitVar("void f() {\n" " FILE *f;" " ftell (f);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: f\n", errout.str()); checkUninitVar("void f( FILE *f) {\n" " ftell (f);\n" "}"); ASSERT_EQUALS("", errout.str()); // puts checkUninitVar("void f() {\n" " char *c;" " puts (c);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void f( char *c) {\n" " puts (c);\n" "}"); ASSERT_EQUALS("", errout.str()); // putchar checkUninitVar("void f() {\n" " char *c;" " putchar (*c);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: c\n", errout.str()); checkUninitVar("void f( char *c) {\n" " putchar (*c);\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestUninitVar) cppcheck-1.66/test/testunusedfunctions.cpp000066400000000000000000000247361236713773000210720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testsuite.h" #include "checkunusedfunctions.h" #include extern std::ostringstream errout; class TestUnusedFunctions : public TestFixture { public: TestUnusedFunctions() : TestFixture("TestUnusedFunctions") { } private: void run() { 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(multipleFiles); // same function name in multiple files TEST_CASE(lineNumber); // Ticket 3059 TEST_CASE(ignore_declaration); // ignore declaration } void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); // 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); checkUnusedFunctions.check(this); } 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("namespace abc {\n" "void foo() { }\n" "};\n" "\n" "int main()\n" "{\n" " f(&abc::foo);\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace abc {\n" "void foo() { }\n" "};\n" "\n" "int main()\n" "{\n" " f = &abc::foo;\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace abc {\n" // #3875 "void foo() { }\n" "};\n" "\n" "int main()\n" "{\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() { }"); ASSERT_EQUALS("", errout.str()); check("int WinMain() { }"); 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()); } 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 multipleFiles() { CheckUnusedFunctions c; // 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(""); Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, fname.str().c_str()); c.parseTokens(tokenizer, "someFile.c", &settings); } // Check for unused functions.. c.check(this); 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.66/test/testunusedprivfunc.cpp000066400000000000000000000515131236713773000207070ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "checkclass.h" #include "testsuite.h" #include extern std::ostringstream errout; class TestUnusedPrivateFunction : public TestFixture { public: TestUnusedPrivateFunction() : TestFixture("TestUnusedPrivateFunction") { } private: void run() { 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 } void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); 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("#file \"p.h\"\n" "class Fred\n" "{\n" "private:\n" " unsigned int f();\n" "public:\n" " Fred();\n" "};\n" "\n" "#endfile\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("#file \"p.h\"\n" "class Fred\n" "{\n" "private:\n" "void f();\n" "};\n" "\n" "\n" "#endfile\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("#file \"p.h\"\n" "class Fred\n" "{\n" "private:\n" "void f();\n" "void g() {}\n" "};\n" "\n" "#endfile\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()); } 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()); } 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" "};"); 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" "};"); 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("[test.cpp:10]: (style) Unused private function: 'InfiniteA::foo'\n", errout.str()); } }; REGISTER_TEST(TestUnusedPrivateFunction) cppcheck-1.66/test/testunusedvar.cpp000066400000000000000000004766561236713773000176660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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 unused variables.. #include "testsuite.h" #include "tokenize.h" #include "checkunusedvar.h" #include extern std::ostringstream errout; class TestUnusedVar : public TestFixture { public: TestUnusedVar() : TestFixture("TestUnusedVar") { } private: void run() { 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(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 #3481 TEST_CASE(localvar42); // ticket #3603 TEST_CASE(localvar43); // ticket #3742 TEST_CASE(localvar44); // ticket #3602 TEST_CASE(localvar45); // ticket #4020 TEST_CASE(localvar46); // ticket #4899 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(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(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(localvarShift2); // x = x >> 1 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(crash1); TEST_CASE(crash2); TEST_CASE(usingNamespace); // #4585 } void checkStructMemberUsage(const char code[]) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); // 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 or union member 'abc::a' is never used.\n" "[test.cpp:4]: (style) struct or union member 'abc::b' is never used.\n" "[test.cpp:5]: (style) struct or 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 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 or union member 'AB::a' is never used.\n", errout.str()); } void functionVariableUsage(const char code[], const char filename[]="test.cpp") { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("style"); // 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"); TODO_ASSERT_EQUALS("[test.c:2]: (style) Variable 'x' is not assigned a value.\n", "[test.c:2]: (style) Variable 'x' is not assigned a value.\n" "[test.c:3]: (style) Variable 'c' is not assigned a value.\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()); } 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() { //garbage code : don't crash functionVariableUsage("{\n" " if (1) = x\n" " else abort s[2]\n" "}"); ASSERT_EQUALS("", errout.str()); } void localvar42() { // #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 localvar43() { // #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 localvar44() { // 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 localvar45() { // #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 localvar46() { // #4899 - FP functionVariableUsage("int func() {\n" " int a = 123;\n" " int b = (short)-a;;\n" " return b;\n" "}"); ASSERT_EQUALS("", 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 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()); } 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()); functionVariableUsage("void foo() {\n" " int i = -1;\n" " int a[] = {1,2,3};\n" " FOREACH_X (int x, a) {\n" " i = x;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'i' is assigned a value that is never used.\n", errout.str()); functionVariableUsage("void foo() {\n" " int i = -1;\n" " int a[] = {1,2,3};\n" " X (int x, a) {\n" " if (i==x) return x;\n" " i = x;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Variable 'i' is assigned a value that is never used.\n", 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" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'x' is assigned a value that is never used.\n", 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 localvarShift2() { functionVariableUsage("int foo()\n" "{\n" " int var = 1;\n" " while (var = var >> 1) { }\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()); } 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()); } 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 #3477 - 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("", 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 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" " 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" "}"); // Don't write an error that "a" is not used ASSERT_EQUALS("", errout.str()); } 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 crash1() { functionVariableUsage("SAL_WNODEPRECATED_DECLARATIONS_PUSH\n" "void convertToTokenArray() {\n" "}\n" "SAL_WNODEPRECATED_DECLARATIONS_POP"); // #4033 } void crash2() { functionVariableUsage("template\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()); } }; REGISTER_TEST(TestUnusedVar) cppcheck-1.66/test/testutils.h000066400000000000000000000026321236713773000164320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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(); } }; #endif // TestUtilsH cppcheck-1.66/test/testvalueflow.cpp000066400000000000000000001057461236713773000176430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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 "testutils.h" #include "valueflow.h" #include "tokenize.h" #include "token.h" #include #include extern std::ostringstream errout; class TestValueFlow : public TestFixture { public: TestValueFlow() : TestFixture("TestValueFlow") { } private: void run() { TEST_CASE(valueFlowNumber); TEST_CASE(valueFlowBitAnd); TEST_CASE(valueFlowCalculations); 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(valueFlowForLoop); TEST_CASE(valueFlowSubFunction); TEST_CASE(valueFlowFunctionReturn); } bool testValueOfX(const char code[], unsigned int linenr, int value) { Settings settings; // strcpy cfg const char cfg[] = "\n" "\n" " \n" ""; settings.library.loadxmldata(cfg, sizeof(cfg)); // 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->intvalue == value) return true; } } } return false; } void bailout(const char code[]) { Settings settings; settings.debugwarnings = true; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); errout.str(""); tokenizer.tokenize(istr, "test.cpp"); } std::list tokenValues(const char code[], const char tokstr[]) { const Settings settings; 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() : ValueFlow::Value(); } void valueFlowNumber() { const char *code; code = "void f() {\n" " x = 123;\n" "}"; ASSERT_EQUALS(123, valueOfTok(code, "123").intvalue); } void valueFlowCalculations() { const char *code; /* code = "void f() {\n" " x = 123+456;\n" "}"; ASSERT_EQUALS(579, valueOfTok(code, "+").intvalue); */ 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); // 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); } } 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)); // bailout: assignment bailout("void f(int x) {\n" " x = y;\n" " if (x == 123) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow 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)); } 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 => dont 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(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 => dont 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 => dont 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("[test.cpp:2]: (debug) ValueFlow 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("[test.cpp:2]: (debug) ValueFlow 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(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("[test.cpp:2]: (debug) ValueFlow 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() { // bailout: global variables bailout("int x;\n" "void f() {\n" " int a = x;\n" " if (x == 123) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (debug) ValueFlow bailout: global variable x\n", errout.str()); // class variable const char *code; 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("[test.cpp:3]: (debug) ValueFlow 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("[test.cpp:3]: (debug) ValueFlow bailout: variable x stopping on return\n", errout.str()); } void valueFlowBeforeConditionMacro() { // bailout: condition is a expanded macro bailout("void f(int x) {\n" " a = x;\n" " $if ($x==$123){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (debug) ValueFlow 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("[test.cpp:4]: (debug) ValueFlow bailout: variable x stopping on goto label\n" "[test.cpp:2]: (debug) ValueFlow 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("[test.cpp:2]: (debug) ValueFlow bailout: assignment of abc\n" "[test.cpp:8]: (debug) ValueFlow bailout: variable abc stopping on goto label\n" "[test.cpp:3]: (debug) ValueFlow 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" " 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)); 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)); // 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)); code = "void f() {\n" " int x = 123;\n" " if (condition1) x = 456;\n" " if (condition2) x = 789;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 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(false) { x = 0; }\n" " else { x->y = 1; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); 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 = 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 () {\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)); // 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 // 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)); } void valueFlowAfterCondition() { const char *code; // 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)); // 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)); // ! 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)); // 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)); // conditional code after if/else/while code = "void f(int x) {\n" " if (x == 2) {}\n" " if (x > 0)\n" " a = x;\n" " else\n" " b = x;\n" "}"; ASSERT_EQUALS(true, 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)); // 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)); // TODO: float code = "void f(float x) {\n" " if (x == 0.5) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); } 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)); } 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(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" " 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" // #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" "}"; 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)); // && 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 == 0\n" " || x) {}" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 9)); } 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) { 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)); // #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)); } void valueFlowFunctionReturn() { const char *code; code = "void f1(int x) {\n" " return x+1;\n" "}\n" "void f2() {\n" " x = 10 - f1(2);\n" "}"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); code = "void 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); } }; REGISTER_TEST(TestValueFlow) cppcheck-1.66/tools/000077500000000000000000000000001236713773000143775ustar00rootroot00000000000000cppcheck-1.66/tools/argparse.py000066400000000000000000002534271236713773000165720ustar00rootroot00000000000000# Author: Steven J. Bethard . """Command-line parsing library This module is an optparse-inspired command-line parsing library that: - handles both optional and positional arguments - produces highly informative usage messages - supports parsers that dispatch to sub-parsers The following is a simple usage example that sums integers from the command-line and writes the result to a file:: parser = argparse.ArgumentParser( description='sum the integers at the command line') parser.add_argument( 'integers', metavar='int', nargs='+', type=int, help='an integer to be summed') parser.add_argument( '--log', default=sys.stdout, type=argparse.FileType('w'), help='the file where the sum should be written') args = parser.parse_args() args.log.write('%s' % sum(args.integers)) args.log.close() The module contains the following public classes: - ArgumentParser -- The main entry point for command-line parsing. As the example above shows, the add_argument() method is used to populate the parser with actions for optional and positional arguments. Then the parse_args() method is invoked to convert the args at the command-line into an object with attributes. - ArgumentError -- The exception raised by ArgumentParser objects when there are errors with the parser's actions. Errors raised while parsing the command-line are caught by ArgumentParser and emitted as command-line messages. - FileType -- A factory for defining types of files to be created. As the example above shows, instances of FileType are typically passed as the type= argument of add_argument() calls. - Action -- The base class for parser actions. Typically actions are selected by passing strings like 'store_true' or 'append_const' to the action= argument of add_argument(). However, for greater customization of ArgumentParser actions, subclasses of Action may be defined and passed as the action= argument. - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, ArgumentDefaultsHelpFormatter -- Formatter classes which may be passed as the formatter_class= argument to the ArgumentParser constructor. HelpFormatter is the default, RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser not to change the formatting for help text, and ArgumentDefaultsHelpFormatter adds information about argument defaults to the help. All other classes in this module are considered implementation details. (Also note that HelpFormatter and RawDescriptionHelpFormatter are only considered public as object names -- the API of the formatter objects is still considered an implementation detail.) """ __version__ = '1.2.1' __all__ = [ 'ArgumentParser', 'ArgumentError', 'ArgumentTypeError', 'FileType', 'HelpFormatter', 'ArgumentDefaultsHelpFormatter', 'RawDescriptionHelpFormatter', 'RawTextHelpFormatter', 'Namespace', 'Action', 'ONE_OR_MORE', 'OPTIONAL', 'PARSER', 'REMAINDER', 'SUPPRESS', 'ZERO_OR_MORE', ] import copy as _copy import os as _os import re as _re import sys as _sys import textwrap as _textwrap from gettext import gettext as _ try: set except NameError: # for python < 2.4 compatibility (sets module is there since 2.3): from sets import Set as set try: basestring except NameError: basestring = str try: sorted except NameError: # for python < 2.4 compatibility: def sorted(iterable, reverse=False): result = list(iterable) result.sort() if reverse: result.reverse() return result def _callable(obj): return hasattr(obj, '__call__') or hasattr(obj, '__bases__') SUPPRESS = '==SUPPRESS==' OPTIONAL = '?' ZERO_OR_MORE = '*' ONE_OR_MORE = '+' PARSER = 'A...' REMAINDER = '...' _UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' # ============================= # Utility functions and classes # ============================= class _AttributeHolder(object): """Abstract base class that provides __repr__. The __repr__ method returns a string in the format:: ClassName(attr=name, attr=name, ...) The attributes are determined either by a class-level attribute, '_kwarg_names', or by inspecting the instance __dict__. """ def __repr__(self): type_name = type(self).__name__ arg_strings = [] for arg in self._get_args(): arg_strings.append(repr(arg)) for name, value in self._get_kwargs(): arg_strings.append('%s=%r' % (name, value)) return '%s(%s)' % (type_name, ', '.join(arg_strings)) def _get_kwargs(self): return sorted(self.__dict__.items()) def _get_args(self): return [] def _ensure_value(namespace, name, value): if getattr(namespace, name, None) is None: setattr(namespace, name, value) return getattr(namespace, name) # =============== # Formatting Help # =============== class HelpFormatter(object): """Formatter for generating usage messages and argument help strings. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def __init__(self, prog, indent_increment=2, max_help_position=24, width=None): # default setting for width if width is None: try: width = int(_os.environ['COLUMNS']) except (KeyError, ValueError): width = 80 width -= 2 self._prog = prog self._indent_increment = indent_increment self._max_help_position = max_help_position self._width = width self._current_indent = 0 self._level = 0 self._action_max_length = 0 self._root_section = self._Section(self, None) self._current_section = self._root_section self._whitespace_matcher = _re.compile(r'\s+') self._long_break_matcher = _re.compile(r'\n\n\n+') # =============================== # Section and indentation methods # =============================== def _indent(self): self._current_indent += self._indent_increment self._level += 1 def _dedent(self): self._current_indent -= self._indent_increment assert self._current_indent >= 0, 'Indent decreased below 0.' self._level -= 1 class _Section(object): def __init__(self, formatter, parent, heading=None): self.formatter = formatter self.parent = parent self.heading = heading self.items = [] def format_help(self): # format the indented section if self.parent is not None: self.formatter._indent() join = self.formatter._join_parts for func, args in self.items: func(*args) item_help = join([func(*args) for func, args in self.items]) if self.parent is not None: self.formatter._dedent() # return nothing if the section was empty if not item_help: return '' # add the heading if the section was non-empty if self.heading is not SUPPRESS and self.heading is not None: current_indent = self.formatter._current_indent heading = '%*s%s:\n' % (current_indent, '', self.heading) else: heading = '' # join the section-initial newline, the heading and the help return join(['\n', heading, item_help, '\n']) def _add_item(self, func, args): self._current_section.items.append((func, args)) # ======================== # Message building methods # ======================== def start_section(self, heading): self._indent() section = self._Section(self, self._current_section, heading) self._add_item(section.format_help, []) self._current_section = section def end_section(self): self._current_section = self._current_section.parent self._dedent() def add_text(self, text): if text is not SUPPRESS and text is not None: self._add_item(self._format_text, [text]) def add_usage(self, usage, actions, groups, prefix=None): if usage is not SUPPRESS: args = usage, actions, groups, prefix self._add_item(self._format_usage, args) def add_argument(self, action): if action.help is not SUPPRESS: # find all invocations get_invocation = self._format_action_invocation invocations = [get_invocation(action)] for subaction in self._iter_indented_subactions(action): invocations.append(get_invocation(subaction)) # update the maximum item length invocation_length = max([len(s) for s in invocations]) action_length = invocation_length + self._current_indent self._action_max_length = max(self._action_max_length, action_length) # add the item to the list self._add_item(self._format_action, [action]) def add_arguments(self, actions): for action in actions: self.add_argument(action) # ======================= # Help-formatting methods # ======================= def format_help(self): help = self._root_section.format_help() if help: help = self._long_break_matcher.sub('\n\n', help) help = help.strip('\n') + '\n' return help def _join_parts(self, part_strings): return ''.join([part for part in part_strings if part and part is not SUPPRESS]) def _format_usage(self, usage, actions, groups, prefix): if prefix is None: prefix = _('usage: ') # if usage is specified, use that if usage is not None: usage = usage % dict(prog=self._prog) # if no optionals or positionals are available, usage is just prog elif usage is None and not actions: usage = '%(prog)s' % dict(prog=self._prog) # if optionals and positionals are available, calculate usage elif usage is None: prog = '%(prog)s' % dict(prog=self._prog) # split optionals from positionals optionals = [] positionals = [] for action in actions: if action.option_strings: optionals.append(action) else: positionals.append(action) # build full usage string format = self._format_actions_usage action_usage = format(optionals + positionals, groups) usage = ' '.join([s for s in [prog, action_usage] if s]) # wrap the usage parts if it's too long text_width = self._width - self._current_indent if len(prefix) + len(usage) > text_width: # break usage into wrappable parts part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' opt_usage = format(optionals, groups) pos_usage = format(positionals, groups) opt_parts = _re.findall(part_regexp, opt_usage) pos_parts = _re.findall(part_regexp, pos_usage) assert ' '.join(opt_parts) == opt_usage assert ' '.join(pos_parts) == pos_usage # helper for wrapping lines def get_lines(parts, indent, prefix=None): lines = [] line = [] if prefix is not None: line_len = len(prefix) - 1 else: line_len = len(indent) - 1 for part in parts: if line_len + 1 + len(part) > text_width: lines.append(indent + ' '.join(line)) line = [] line_len = len(indent) - 1 line.append(part) line_len += len(part) + 1 if line: lines.append(indent + ' '.join(line)) if prefix is not None: lines[0] = lines[0][len(indent):] return lines # if prog is short, follow it with optionals or positionals if len(prefix) + len(prog) <= 0.75 * text_width: indent = ' ' * (len(prefix) + len(prog) + 1) if opt_parts: lines = get_lines([prog] + opt_parts, indent, prefix) lines.extend(get_lines(pos_parts, indent)) elif pos_parts: lines = get_lines([prog] + pos_parts, indent, prefix) else: lines = [prog] # if prog is long, put it on its own line else: indent = ' ' * len(prefix) parts = opt_parts + pos_parts lines = get_lines(parts, indent) if len(lines) > 1: lines = [] lines.extend(get_lines(opt_parts, indent)) lines.extend(get_lines(pos_parts, indent)) lines = [prog] + lines # join lines into usage usage = '\n'.join(lines) # prefix with 'usage:' return '%s%s\n\n' % (prefix, usage) def _format_actions_usage(self, actions, groups): # find group indices and identify actions in groups group_actions = set() inserts = {} for group in groups: try: start = actions.index(group._group_actions[0]) except ValueError: continue else: end = start + len(group._group_actions) if actions[start:end] == group._group_actions: for action in group._group_actions: group_actions.add(action) if not group.required: if start in inserts: inserts[start] += ' [' else: inserts[start] = '[' inserts[end] = ']' else: if start in inserts: inserts[start] += ' (' else: inserts[start] = '(' inserts[end] = ')' for i in range(start + 1, end): inserts[i] = '|' # collect all actions format strings parts = [] for i, action in enumerate(actions): # suppressed arguments are marked with None # remove | separators for suppressed arguments if action.help is SUPPRESS: parts.append(None) if inserts.get(i) == '|': inserts.pop(i) elif inserts.get(i + 1) == '|': inserts.pop(i + 1) # produce all arg strings elif not action.option_strings: part = self._format_args(action, action.dest) # if it's in a group, strip the outer [] if action in group_actions: if part[0] == '[' and part[-1] == ']': part = part[1:-1] # add the action string to the list parts.append(part) # produce the first way to invoke the option in brackets else: option_string = action.option_strings[0] # if the Optional doesn't take a value, format is: # -s or --long if action.nargs == 0: part = '%s' % option_string # if the Optional takes a value, format is: # -s ARGS or --long ARGS else: default = action.dest.upper() args_string = self._format_args(action, default) part = '%s %s' % (option_string, args_string) # make it look optional if it's not required or in a group if not action.required and action not in group_actions: part = '[%s]' % part # add the action string to the list parts.append(part) # insert things at the necessary indices for i in sorted(inserts, reverse=True): parts[i:i] = [inserts[i]] # join all the action items with spaces text = ' '.join([item for item in parts if item is not None]) # clean up separators for mutually exclusive groups open = r'[\[(]' close = r'[\])]' text = _re.sub(r'(%s) ' % open, r'\1', text) text = _re.sub(r' (%s)' % close, r'\1', text) text = _re.sub(r'%s *%s' % (open, close), r'', text) text = _re.sub(r'\(([^|]*)\)', r'\1', text) text = text.strip() # return the text return text def _format_text(self, text): if '%(prog)' in text: text = text % dict(prog=self._prog) text_width = self._width - self._current_indent indent = ' ' * self._current_indent return self._fill_text(text, text_width, indent) + '\n\n' def _format_action(self, action): # determine the required width and the entry label help_position = min(self._action_max_length + 2, self._max_help_position) help_width = self._width - help_position action_width = help_position - self._current_indent - 2 action_header = self._format_action_invocation(action) # ho nelp; start on same line and add a final newline if not action.help: tup = self._current_indent, '', action_header action_header = '%*s%s\n' % tup # short action name; start on the same line and pad two spaces elif len(action_header) <= action_width: tup = self._current_indent, '', action_width, action_header action_header = '%*s%-*s ' % tup indent_first = 0 # long action name; start on the next line else: tup = self._current_indent, '', action_header action_header = '%*s%s\n' % tup indent_first = help_position # collect the pieces of the action help parts = [action_header] # if there was help for the action, add lines of help text if action.help: help_text = self._expand_help(action) help_lines = self._split_lines(help_text, help_width) parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) for line in help_lines[1:]: parts.append('%*s%s\n' % (help_position, '', line)) # or add a newline if the description doesn't end with one elif not action_header.endswith('\n'): parts.append('\n') # if there are any sub-actions, add their help as well for subaction in self._iter_indented_subactions(action): parts.append(self._format_action(subaction)) # return a single string return self._join_parts(parts) def _format_action_invocation(self, action): if not action.option_strings: metavar, = self._metavar_formatter(action, action.dest)(1) return metavar else: parts = [] # if the Optional doesn't take a value, format is: # -s, --long if action.nargs == 0: parts.extend(action.option_strings) # if the Optional takes a value, format is: # -s ARGS, --long ARGS else: default = action.dest.upper() args_string = self._format_args(action, default) for option_string in action.option_strings: parts.append('%s %s' % (option_string, args_string)) return ', '.join(parts) def _metavar_formatter(self, action, default_metavar): if action.metavar is not None: result = action.metavar elif action.choices is not None: choice_strs = [str(choice) for choice in action.choices] result = '{%s}' % ','.join(choice_strs) else: result = default_metavar def format(tuple_size): if isinstance(result, tuple): return result else: return (result, ) * tuple_size return format def _format_args(self, action, default_metavar): get_metavar = self._metavar_formatter(action, default_metavar) if action.nargs is None: result = '%s' % get_metavar(1) elif action.nargs == OPTIONAL: result = '[%s]' % get_metavar(1) elif action.nargs == ZERO_OR_MORE: result = '[%s [%s ...]]' % get_metavar(2) elif action.nargs == ONE_OR_MORE: result = '%s [%s ...]' % get_metavar(2) elif action.nargs == REMAINDER: result = '...' elif action.nargs == PARSER: result = '%s ...' % get_metavar(1) else: formats = ['%s' for _ in range(action.nargs)] result = ' '.join(formats) % get_metavar(action.nargs) return result def _expand_help(self, action): params = dict(vars(action), prog=self._prog) for name in list(params): if params[name] is SUPPRESS: del params[name] for name in list(params): if hasattr(params[name], '__name__'): params[name] = params[name].__name__ if params.get('choices') is not None: choices_str = ', '.join([str(c) for c in params['choices']]) params['choices'] = choices_str return self._get_help_string(action) % params def _iter_indented_subactions(self, action): try: get_subactions = action._get_subactions except AttributeError: pass else: self._indent() for subaction in get_subactions(): yield subaction self._dedent() def _split_lines(self, text, width): text = self._whitespace_matcher.sub(' ', text).strip() return _textwrap.wrap(text, width) def _fill_text(self, text, width, indent): text = self._whitespace_matcher.sub(' ', text).strip() return _textwrap.fill(text, width, initial_indent=indent, subsequent_indent=indent) def _get_help_string(self, action): return action.help class RawDescriptionHelpFormatter(HelpFormatter): """Help message formatter which retains any formatting in descriptions. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def _fill_text(self, text, width, indent): return ''.join([indent + line for line in text.splitlines(True)]) class RawTextHelpFormatter(RawDescriptionHelpFormatter): """Help message formatter which retains formatting of all help text. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def _split_lines(self, text, width): return text.splitlines() class ArgumentDefaultsHelpFormatter(HelpFormatter): """Help message formatter which adds default values to argument help. Only the name of this class is considered a public API. All the methods provided by the class are considered an implementation detail. """ def _get_help_string(self, action): help = action.help if '%(default)' not in action.help: if action.default is not SUPPRESS: defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] if action.option_strings or action.nargs in defaulting_nargs: help += ' (default: %(default)s)' return help # ===================== # Options and Arguments # ===================== def _get_action_name(argument): if argument is None: return None elif argument.option_strings: return '/'.join(argument.option_strings) elif argument.metavar not in (None, SUPPRESS): return argument.metavar elif argument.dest not in (None, SUPPRESS): return argument.dest else: return None class ArgumentError(Exception): """An error from creating or using an argument (optional or positional). The string value of this exception is the message, augmented with information about the argument that caused it. """ def __init__(self, argument, message): self.argument_name = _get_action_name(argument) self.message = message def __str__(self): if self.argument_name is None: format = '%(message)s' else: format = 'argument %(argument_name)s: %(message)s' return format % dict(message=self.message, argument_name=self.argument_name) class ArgumentTypeError(Exception): """An error from trying to convert a command line string to a type.""" pass # ============== # Action classes # ============== class Action(_AttributeHolder): """Information about how to convert command line strings to Python objects. Action objects are used by an ArgumentParser to represent the information needed to parse a single argument from one or more strings from the command line. The keyword arguments to the Action constructor are also all attributes of Action instances. Keyword Arguments: - option_strings -- A list of command-line option strings which should be associated with this action. - dest -- The name of the attribute to hold the created object(s) - nargs -- The number of command-line arguments that should be consumed. By default, one argument will be consumed and a single value will be produced. Other values include: - N (an integer) consumes N arguments (and produces a list) - '?' consumes zero or one arguments - '*' consumes zero or more arguments (and produces a list) - '+' consumes one or more arguments (and produces a list) Note that the difference between the default and nargs=1 is that with the default, a single value will be produced, while with nargs=1, a list containing a single value will be produced. - const -- The value to be produced if the option is specified and the option uses an action that takes no values. - default -- The value to be produced if the option is not specified. - type -- The type which the command-line arguments should be converted to, should be one of 'string', 'int', 'float', 'complex' or a callable object that accepts a single string argument. If None, 'string' is assumed. - choices -- A container of values that should be allowed. If not None, after a command-line argument has been converted to the appropriate type, an exception will be raised if it is not a member of this collection. - required -- True if the action must always be specified at the command line. This is only meaningful for optional command-line arguments. - help -- The help string describing the argument. - metavar -- The name to be used for the option's argument with the help string. If None, the 'dest' value will be used as the name. """ def __init__(self, option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None): self.option_strings = option_strings self.dest = dest self.nargs = nargs self.const = const self.default = default self.type = type self.choices = choices self.required = required self.help = help self.metavar = metavar def _get_kwargs(self): names = [ 'option_strings', 'dest', 'nargs', 'const', 'default', 'type', 'choices', 'help', 'metavar', ] return [(name, getattr(self, name)) for name in names] def __call__(self, parser, namespace, values, option_string=None): raise NotImplementedError(_('.__call__() not defined')) class _StoreAction(Action): def __init__(self, option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None): if nargs == 0: raise ValueError('nargs for store actions must be > 0; if you ' 'have nothing to store, actions such as store ' 'true or store const may be more appropriate') if const is not None and nargs != OPTIONAL: raise ValueError('nargs must be %r to supply const' % OPTIONAL) super(_StoreAction, self).__init__( option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) class _StoreConstAction(Action): def __init__(self, option_strings, dest, const, default=None, required=False, help=None, metavar=None): super(_StoreConstAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, const=const, default=default, required=required, help=help) def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, self.const) class _StoreTrueAction(_StoreConstAction): def __init__(self, option_strings, dest, default=False, required=False, help=None): super(_StoreTrueAction, self).__init__( option_strings=option_strings, dest=dest, const=True, default=default, required=required, help=help) class _StoreFalseAction(_StoreConstAction): def __init__(self, option_strings, dest, default=True, required=False, help=None): super(_StoreFalseAction, self).__init__( option_strings=option_strings, dest=dest, const=False, default=default, required=required, help=help) class _AppendAction(Action): def __init__(self, option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None): if nargs == 0: raise ValueError('nargs for append actions must be > 0; if arg ' 'strings are not supplying the value to append, ' 'the append const action may be more appropriate') if const is not None and nargs != OPTIONAL: raise ValueError('nargs must be %r to supply const' % OPTIONAL) super(_AppendAction, self).__init__( option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) def __call__(self, parser, namespace, values, option_string=None): items = _copy.copy(_ensure_value(namespace, self.dest, [])) items.append(values) setattr(namespace, self.dest, items) class _AppendConstAction(Action): def __init__(self, option_strings, dest, const, default=None, required=False, help=None, metavar=None): super(_AppendConstAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, const=const, default=default, required=required, help=help, metavar=metavar) def __call__(self, parser, namespace, values, option_string=None): items = _copy.copy(_ensure_value(namespace, self.dest, [])) items.append(self.const) setattr(namespace, self.dest, items) class _CountAction(Action): def __init__(self, option_strings, dest, default=None, required=False, help=None): super(_CountAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, default=default, required=required, help=help) def __call__(self, parser, namespace, values, option_string=None): new_count = _ensure_value(namespace, self.dest, 0) + 1 setattr(namespace, self.dest, new_count) class _HelpAction(Action): def __init__(self, option_strings, dest=SUPPRESS, default=SUPPRESS, help=None): super(_HelpAction, self).__init__( option_strings=option_strings, dest=dest, default=default, nargs=0, help=help) def __call__(self, parser, namespace, values, option_string=None): parser.print_help() parser.exit() class _VersionAction(Action): def __init__(self, option_strings, version=None, dest=SUPPRESS, default=SUPPRESS, help="show program's version number and exit"): super(_VersionAction, self).__init__( option_strings=option_strings, dest=dest, default=default, nargs=0, help=help) self.version = version def __call__(self, parser, namespace, values, option_string=None): version = self.version if version is None: version = parser.version formatter = parser._get_formatter() formatter.add_text(version) parser.exit(message=formatter.format_help()) class _SubParsersAction(Action): class _ChoicesPseudoAction(Action): def __init__(self, name, help): sup = super(_SubParsersAction._ChoicesPseudoAction, self) sup.__init__(option_strings=[], dest=name, help=help) def __init__(self, option_strings, prog, parser_class, dest=SUPPRESS, help=None, metavar=None): self._prog_prefix = prog self._parser_class = parser_class self._name_parser_map = {} self._choices_actions = [] super(_SubParsersAction, self).__init__( option_strings=option_strings, dest=dest, nargs=PARSER, choices=self._name_parser_map, help=help, metavar=metavar) def add_parser(self, name, **kwargs): # set prog from the existing prefix if kwargs.get('prog') is None: kwargs['prog'] = '%s %s' % (self._prog_prefix, name) # create a pseudo-action to hold the choice help if 'help' in kwargs: help = kwargs.pop('help') choice_action = self._ChoicesPseudoAction(name, help) self._choices_actions.append(choice_action) # create the parser and add it to the map parser = self._parser_class(**kwargs) self._name_parser_map[name] = parser return parser def _get_subactions(self): return self._choices_actions def __call__(self, parser, namespace, values, option_string=None): parser_name = values[0] arg_strings = values[1:] # set the parser name if requested if self.dest is not SUPPRESS: setattr(namespace, self.dest, parser_name) # select the parser try: parser = self._name_parser_map[parser_name] except KeyError: tup = parser_name, ', '.join(self._name_parser_map) msg = _('unknown parser %r (choices: %s)' % tup) raise ArgumentError(self, msg) # parse all the remaining options into the namespace # store any unrecognized options on the object, so that the top # level parser can decide what to do with them namespace, arg_strings = parser.parse_known_args( arg_strings, namespace) if arg_strings: vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) # ============== # Type classes # ============== class FileType(object): """Factory for creating file object types Instances of FileType are typically passed as type= arguments to the ArgumentParser add_argument() method. Keyword Arguments: - mode -- A string indicating how the file is to be opened. Accepts the same values as the builtin open() function. - bufsize -- The file's desired buffer size. Accepts the same values as the builtin open() function. """ def __init__(self, mode='r', bufsize=None): self._mode = mode self._bufsize = bufsize def __call__(self, string): # the special argument "-" means sys.std{in,out} if string == '-': if 'r' in self._mode: return _sys.stdin elif 'w' in self._mode: return _sys.stdout else: msg = _('argument "-" with mode %r' % self._mode) raise ValueError(msg) # all other arguments are used as file names if self._bufsize: return open(string, self._mode, self._bufsize) else: return open(string, self._mode) def __repr__(self): args = [self._mode, self._bufsize] args_str = ', '.join([repr(arg) for arg in args if arg is not None]) return '%s(%s)' % (type(self).__name__, args_str) # =========================== # Optional and Positional Parsing # =========================== class Namespace(_AttributeHolder): """Simple object for storing attributes. Implements equality by attribute names and values, and provides a simple string representation. """ def __init__(self, **kwargs): for name in kwargs: setattr(self, name, kwargs[name]) __hash__ = None def __eq__(self, other): return vars(self) == vars(other) def __ne__(self, other): return not (self == other) def __contains__(self, key): return key in self.__dict__ class _ActionsContainer(object): def __init__(self, description, prefix_chars, argument_default, conflict_handler): super(_ActionsContainer, self).__init__() self.description = description self.argument_default = argument_default self.prefix_chars = prefix_chars self.conflict_handler = conflict_handler # set up registries self._registries = {} # register actions self.register('action', None, _StoreAction) self.register('action', 'store', _StoreAction) self.register('action', 'store_const', _StoreConstAction) self.register('action', 'store_true', _StoreTrueAction) self.register('action', 'store_false', _StoreFalseAction) self.register('action', 'append', _AppendAction) self.register('action', 'append_const', _AppendConstAction) self.register('action', 'count', _CountAction) self.register('action', 'help', _HelpAction) self.register('action', 'version', _VersionAction) self.register('action', 'parsers', _SubParsersAction) # raise an exception if the conflict handler is invalid self._get_handler() # action storage self._actions = [] self._option_string_actions = {} # groups self._action_groups = [] self._mutually_exclusive_groups = [] # defaults storage self._defaults = {} # determines whether an "option" looks like a negative number self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') # whether or not there are any optionals that look like negative # numbers -- uses a list so it can be shared and edited self._has_negative_number_optionals = [] # ==================== # Registration methods # ==================== def register(self, registry_name, value, object): registry = self._registries.setdefault(registry_name, {}) registry[value] = object def _registry_get(self, registry_name, value, default=None): return self._registries[registry_name].get(value, default) # ================================== # Namespace default accessor methods # ================================== def set_defaults(self, **kwargs): self._defaults.update(kwargs) # if these defaults match any existing arguments, replace # the previous default on the object with the new one for action in self._actions: if action.dest in kwargs: action.default = kwargs[action.dest] def get_default(self, dest): for action in self._actions: if action.dest == dest and action.default is not None: return action.default return self._defaults.get(dest, None) # ======================= # Adding argument actions # ======================= def add_argument(self, *args, **kwargs): """ add_argument(dest, ..., name=value, ...) add_argument(option_string, option_string, ..., name=value, ...) """ # if no positional args are supplied or only one is supplied and # it doesn't look like an option string, parse a positional # argument chars = self.prefix_chars if not args or len(args) == 1 and args[0][0] not in chars: if args and 'dest' in kwargs: raise ValueError('dest supplied twice for positional argument') kwargs = self._get_positional_kwargs(*args, **kwargs) # otherwise, we're adding an optional argument else: kwargs = self._get_optional_kwargs(*args, **kwargs) # if no default was supplied, use the parser-level default if 'default' not in kwargs: dest = kwargs['dest'] if dest in self._defaults: kwargs['default'] = self._defaults[dest] elif self.argument_default is not None: kwargs['default'] = self.argument_default # create the action object, and add it to the parser action_class = self._pop_action_class(kwargs) if not _callable(action_class): raise ValueError('unknown action "%s"' % action_class) action = action_class(**kwargs) # raise an error if the action type is not callable type_func = self._registry_get('type', action.type, action.type) if not _callable(type_func): raise ValueError('%r is not callable' % type_func) return self._add_action(action) def add_argument_group(self, *args, **kwargs): group = _ArgumentGroup(self, *args, **kwargs) self._action_groups.append(group) return group def add_mutually_exclusive_group(self, **kwargs): group = _MutuallyExclusiveGroup(self, **kwargs) self._mutually_exclusive_groups.append(group) return group def _add_action(self, action): # resolve any conflicts self._check_conflict(action) # add to actions list self._actions.append(action) action.container = self # index the action by any option strings it has for option_string in action.option_strings: self._option_string_actions[option_string] = action # set the flag if any option strings look like negative numbers for option_string in action.option_strings: if self._negative_number_matcher.match(option_string): if not self._has_negative_number_optionals: self._has_negative_number_optionals.append(True) # return the created action return action def _remove_action(self, action): self._actions.remove(action) def _add_container_actions(self, container): # collect groups by titles title_group_map = {} for group in self._action_groups: if group.title in title_group_map: msg = _('cannot merge actions - two groups are named %r') raise ValueError(msg % (group.title)) title_group_map[group.title] = group # map each action to its group group_map = {} for group in container._action_groups: # if a group with the title exists, use that, otherwise # create a new group matching the container's group if group.title not in title_group_map: title_group_map[group.title] = self.add_argument_group( title=group.title, description=group.description, conflict_handler=group.conflict_handler) # map the actions to their new group for action in group._group_actions: group_map[action] = title_group_map[group.title] # add container's mutually exclusive groups # NOTE: if add_mutually_exclusive_group ever gains title= and # description= then this code will need to be expanded as above for group in container._mutually_exclusive_groups: mutex_group = self.add_mutually_exclusive_group( required=group.required) # map the actions to their new mutex group for action in group._group_actions: group_map[action] = mutex_group # add all actions to this container or their group for action in container._actions: group_map.get(action, self)._add_action(action) def _get_positional_kwargs(self, dest, **kwargs): # make sure required is not specified if 'required' in kwargs: msg = _("'required' is an invalid argument for positionals") raise TypeError(msg) # mark positional arguments as required if at least one is # always required if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: kwargs['required'] = True if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: kwargs['required'] = True # return the keyword arguments with no option strings return dict(kwargs, dest=dest, option_strings=[]) def _get_optional_kwargs(self, *args, **kwargs): # determine short and long option strings option_strings = [] long_option_strings = [] for option_string in args: # error on strings that don't start with an appropriate prefix if not option_string[0] in self.prefix_chars: msg = _('invalid option string %r: ' 'must start with a character %r') tup = option_string, self.prefix_chars raise ValueError(msg % tup) # strings starting with two prefix characters are long options option_strings.append(option_string) if option_string[0] in self.prefix_chars: if len(option_string) > 1: if option_string[1] in self.prefix_chars: long_option_strings.append(option_string) # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' dest = kwargs.pop('dest', None) if dest is None: if long_option_strings: dest_option_string = long_option_strings[0] else: dest_option_string = option_strings[0] dest = dest_option_string.lstrip(self.prefix_chars) if not dest: msg = _('dest= is required for options like %r') raise ValueError(msg % option_string) dest = dest.replace('-', '_') # return the updated keyword arguments return dict(kwargs, dest=dest, option_strings=option_strings) def _pop_action_class(self, kwargs, default=None): action = kwargs.pop('action', default) return self._registry_get('action', action, action) def _get_handler(self): # determine function from conflict handler string handler_func_name = '_handle_conflict_%s' % self.conflict_handler try: return getattr(self, handler_func_name) except AttributeError: msg = _('invalid conflict_resolution value: %r') raise ValueError(msg % self.conflict_handler) def _check_conflict(self, action): # find all options that conflict with this option confl_optionals = [] for option_string in action.option_strings: if option_string in self._option_string_actions: confl_optional = self._option_string_actions[option_string] confl_optionals.append((option_string, confl_optional)) # resolve any conflicts if confl_optionals: conflict_handler = self._get_handler() conflict_handler(action, confl_optionals) def _handle_conflict_error(self, action, conflicting_actions): message = _('conflicting option string(s): %s') conflict_string = ', '.join([option_string for option_string, action in conflicting_actions]) raise ArgumentError(action, message % conflict_string) def _handle_conflict_resolve(self, action, conflicting_actions): # remove all conflicting options for option_string, action in conflicting_actions: # remove the conflicting option action.option_strings.remove(option_string) self._option_string_actions.pop(option_string, None) # if the option now has no option string, remove it from the # container holding it if not action.option_strings: action.container._remove_action(action) class _ArgumentGroup(_ActionsContainer): def __init__(self, container, title=None, description=None, **kwargs): # add any missing keyword arguments by checking the container update = kwargs.setdefault update('conflict_handler', container.conflict_handler) update('prefix_chars', container.prefix_chars) update('argument_default', container.argument_default) super_init = super(_ArgumentGroup, self).__init__ super_init(description=description, **kwargs) # group attributes self.title = title self._group_actions = [] # share most attributes with the container self._registries = container._registries self._actions = container._actions self._option_string_actions = container._option_string_actions self._defaults = container._defaults self._has_negative_number_optionals = \ container._has_negative_number_optionals def _add_action(self, action): action = super(_ArgumentGroup, self)._add_action(action) self._group_actions.append(action) return action def _remove_action(self, action): super(_ArgumentGroup, self)._remove_action(action) self._group_actions.remove(action) class _MutuallyExclusiveGroup(_ArgumentGroup): def __init__(self, container, required=False): super(_MutuallyExclusiveGroup, self).__init__(container) self.required = required self._container = container def _add_action(self, action): if action.required: msg = _('mutually exclusive arguments must be optional') raise ValueError(msg) action = self._container._add_action(action) self._group_actions.append(action) return action def _remove_action(self, action): self._container._remove_action(action) self._group_actions.remove(action) class ArgumentParser(_AttributeHolder, _ActionsContainer): """Object for parsing command line strings into Python objects. Keyword Arguments: - prog -- The name of the program (default: sys.argv[0]) - usage -- A usage message (default: auto-generated from arguments) - description -- A description of what the program does - epilog -- Text following the argument descriptions - parents -- Parsers whose arguments should be copied into this one - formatter_class -- HelpFormatter class for printing help messages - prefix_chars -- Characters that prefix optional arguments - fromfile_prefix_chars -- Characters that prefix files containing additional arguments - argument_default -- The default value for all arguments - conflict_handler -- String indicating how to handle conflicts - add_help -- Add a -h/-help option """ def __init__(self, prog=None, usage=None, description=None, epilog=None, version=None, parents=[], formatter_class=HelpFormatter, prefix_chars='-', fromfile_prefix_chars=None, argument_default=None, conflict_handler='error', add_help=True): if version is not None: import warnings warnings.warn( """The "version" argument to ArgumentParser is deprecated. """ """Please use """ """"add_argument(..., action='version', version="N", ...)" """ """instead""", DeprecationWarning) superinit = super(ArgumentParser, self).__init__ superinit(description=description, prefix_chars=prefix_chars, argument_default=argument_default, conflict_handler=conflict_handler) # default setting for prog if prog is None: prog = _os.path.basename(_sys.argv[0]) self.prog = prog self.usage = usage self.epilog = epilog self.version = version self.formatter_class = formatter_class self.fromfile_prefix_chars = fromfile_prefix_chars self.add_help = add_help add_group = self.add_argument_group self._positionals = add_group(_('positional arguments')) self._optionals = add_group(_('optional arguments')) self._subparsers = None # register types def identity(string): return string self.register('type', None, identity) # add help and version arguments if necessary # (using explicit default to override global argument_default) if '-' in prefix_chars: default_prefix = '-' else: default_prefix = prefix_chars[0] if self.add_help: self.add_argument( default_prefix + 'h', default_prefix * 2 + 'help', action='help', default=SUPPRESS, help=_('show this help message and exit')) if self.version: self.add_argument( default_prefix + 'v', default_prefix * 2 + 'version', action='version', default=SUPPRESS, version=self.version, help=_("show program's version number and exit")) # add parent arguments and defaults for parent in parents: self._add_container_actions(parent) try: defaults = parent._defaults except AttributeError: pass else: self._defaults.update(defaults) # ======================= # Pretty __repr__ methods # ======================= def _get_kwargs(self): names = [ 'prog', 'usage', 'description', 'version', 'formatter_class', 'conflict_handler', 'add_help', ] return [(name, getattr(self, name)) for name in names] # ================================== # Optional/Positional adding methods # ================================== def add_subparsers(self, **kwargs): if self._subparsers is not None: self.error(_('cannot have multiple subparser arguments')) # add the parser class to the arguments if it's not present kwargs.setdefault('parser_class', type(self)) if 'title' in kwargs or 'description' in kwargs: title = _(kwargs.pop('title', 'subcommands')) description = _(kwargs.pop('description', None)) self._subparsers = self.add_argument_group(title, description) else: self._subparsers = self._positionals # prog defaults to the usage message of this parser, skipping # optional arguments and with no "usage:" prefix if kwargs.get('prog') is None: formatter = self._get_formatter() positionals = self._get_positional_actions() groups = self._mutually_exclusive_groups formatter.add_usage(self.usage, positionals, groups, '') kwargs['prog'] = formatter.format_help().strip() # create the parsers action and add it to the positionals list parsers_class = self._pop_action_class(kwargs, 'parsers') action = parsers_class(option_strings=[], **kwargs) self._subparsers._add_action(action) # return the created parsers action return action def _add_action(self, action): if action.option_strings: self._optionals._add_action(action) else: self._positionals._add_action(action) return action def _get_optional_actions(self): return [action for action in self._actions if action.option_strings] def _get_positional_actions(self): return [action for action in self._actions if not action.option_strings] # ===================================== # Command line argument parsing methods # ===================================== def parse_args(self, args=None, namespace=None): args, argv = self.parse_known_args(args, namespace) if argv: msg = _('unrecognized arguments: %s') self.error(msg % ' '.join(argv)) return args def parse_known_args(self, args=None, namespace=None): # args default to the system args if args is None: args = _sys.argv[1:] # default Namespace built from parser defaults if namespace is None: namespace = Namespace() # add any action defaults that aren't present for action in self._actions: if action.dest is not SUPPRESS: if not hasattr(namespace, action.dest): if action.default is not SUPPRESS: default = action.default if isinstance(action.default, basestring): default = self._get_value(action, default) setattr(namespace, action.dest, default) # add any parser defaults that aren't present for dest in self._defaults: if not hasattr(namespace, dest): setattr(namespace, dest, self._defaults[dest]) # parse the arguments and exit if there are any errors try: namespace, args = self._parse_known_args(args, namespace) if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) return namespace, args except ArgumentError: err = _sys.exc_info()[1] self.error(str(err)) def _parse_known_args(self, arg_strings, namespace): # replace arg strings that are file references if self.fromfile_prefix_chars is not None: arg_strings = self._read_args_from_files(arg_strings) # map all mutually exclusive arguments to the other arguments # they can't occur with action_conflicts = {} for mutex_group in self._mutually_exclusive_groups: group_actions = mutex_group._group_actions for i, mutex_action in enumerate(mutex_group._group_actions): conflicts = action_conflicts.setdefault(mutex_action, []) conflicts.extend(group_actions[:i]) conflicts.extend(group_actions[i + 1:]) # find all option indices, and determine the arg_string_pattern # which has an 'O' if there is an option at an index, # an 'A' if there is an argument, or a '-' if there is a '--' option_string_indices = {} arg_string_pattern_parts = [] arg_strings_iter = iter(arg_strings) for i, arg_string in enumerate(arg_strings_iter): # all args after -- are non-options if arg_string == '--': arg_string_pattern_parts.append('-') for arg_string in arg_strings_iter: arg_string_pattern_parts.append('A') # otherwise, add the arg to the arg strings # and note the index if it was an option else: option_tuple = self._parse_optional(arg_string) if option_tuple is None: pattern = 'A' else: option_string_indices[i] = option_tuple pattern = 'O' arg_string_pattern_parts.append(pattern) # join the pieces together to form the pattern arg_strings_pattern = ''.join(arg_string_pattern_parts) # converts arg strings to the appropriate and then takes the action seen_actions = set() seen_non_default_actions = set() def take_action(action, argument_strings, option_string=None): seen_actions.add(action) argument_values = self._get_values(action, argument_strings) # error if this argument is not allowed with other previously # seen arguments, assuming that actions that use the default # value don't really count as "present" if argument_values is not action.default: seen_non_default_actions.add(action) for conflict_action in action_conflicts.get(action, []): if conflict_action in seen_non_default_actions: msg = _('not allowed with argument %s') action_name = _get_action_name(conflict_action) raise ArgumentError(action, msg % action_name) # take the action if we didn't receive a SUPPRESS value # (e.g. from a default) if argument_values is not SUPPRESS: action(self, namespace, argument_values, option_string) # function to convert arg_strings into an optional action def consume_optional(start_index): # get the optional identified at this index option_tuple = option_string_indices[start_index] action, option_string, explicit_arg = option_tuple # identify additional optionals in the same arg string # (e.g. -xyz is the same as -x -y -z if no args are required) match_argument = self._match_argument action_tuples = [] while True: # if we found no optional action, skip it if action is None: extras.append(arg_strings[start_index]) return start_index + 1 # if there is an explicit argument, try to match the # optional's string arguments to only this if explicit_arg is not None: arg_count = match_argument(action, 'A') # if the action is a single-dash option and takes no # arguments, try to parse more single-dash options out # of the tail of the option string chars = self.prefix_chars if arg_count == 0 and option_string[1] not in chars: action_tuples.append((action, [], option_string)) char = option_string[0] option_string = char + explicit_arg[0] new_explicit_arg = explicit_arg[1:] or None optionals_map = self._option_string_actions if option_string in optionals_map: action = optionals_map[option_string] explicit_arg = new_explicit_arg else: msg = _('ignored explicit argument %r') raise ArgumentError(action, msg % explicit_arg) # if the action expect exactly one argument, we've # successfully matched the option; exit the loop elif arg_count == 1: stop = start_index + 1 args = [explicit_arg] action_tuples.append((action, args, option_string)) break # error if a double-dash option did not use the # explicit argument else: msg = _('ignored explicit argument %r') raise ArgumentError(action, msg % explicit_arg) # if there is no explicit argument, try to match the # optional's string arguments with the following strings # if successful, exit the loop else: start = start_index + 1 selected_patterns = arg_strings_pattern[start:] arg_count = match_argument(action, selected_patterns) stop = start + arg_count args = arg_strings[start:stop] action_tuples.append((action, args, option_string)) break # add the Optional to the list and return the index at which # the Optional's string args stopped assert action_tuples for action, args, option_string in action_tuples: take_action(action, args, option_string) return stop # the list of Positionals left to be parsed; this is modified # by consume_positionals() positionals = self._get_positional_actions() # function to convert arg_strings into positional actions def consume_positionals(start_index): # match as many Positionals as possible match_partial = self._match_arguments_partial selected_pattern = arg_strings_pattern[start_index:] arg_counts = match_partial(positionals, selected_pattern) # slice off the appropriate arg strings for each Positional # and add the Positional and its args to the list for action, arg_count in zip(positionals, arg_counts): args = arg_strings[start_index: start_index + arg_count] start_index += arg_count take_action(action, args) # slice off the Positionals that we just parsed and return the # index at which the Positionals' string args stopped positionals[:] = positionals[len(arg_counts):] return start_index # consume Positionals and Optionals alternately, until we have # passed the last option string extras = [] start_index = 0 if option_string_indices: max_option_string_index = max(option_string_indices) else: max_option_string_index = -1 while start_index <= max_option_string_index: # consume any Positionals preceding the next option next_option_string_index = min([ index for index in option_string_indices if index >= start_index]) if start_index != next_option_string_index: positionals_end_index = consume_positionals(start_index) # only try to parse the next optional if we didn't consume # the option string during the positionals parsing if positionals_end_index > start_index: start_index = positionals_end_index continue else: start_index = positionals_end_index # if we consumed all the positionals we could and we're not # at the index of an option string, there were extra arguments if start_index not in option_string_indices: strings = arg_strings[start_index:next_option_string_index] extras.extend(strings) start_index = next_option_string_index # consume the next optional and any arguments for it start_index = consume_optional(start_index) # consume any positionals following the last Optional stop_index = consume_positionals(start_index) # if we didn't consume all the argument strings, there were extras extras.extend(arg_strings[stop_index:]) # if we didn't use all the Positional objects, there were too few # arg strings supplied. if positionals: self.error(_('too few arguments')) # make sure all required actions were present for action in self._actions: if action.required: if action not in seen_actions: name = _get_action_name(action) self.error(_('argument %s is required') % name) # make sure all required groups had one option present for group in self._mutually_exclusive_groups: if group.required: for action in group._group_actions: if action in seen_non_default_actions: break # if no actions were used, report the error else: names = [_get_action_name(action) for action in group._group_actions if action.help is not SUPPRESS] msg = _('one of the arguments %s is required') self.error(msg % ' '.join(names)) # return the updated namespace and the extra arguments return namespace, extras def _read_args_from_files(self, arg_strings): # expand arguments referencing files new_arg_strings = [] for arg_string in arg_strings: # for regular arguments, just add them back into the list if arg_string[0] not in self.fromfile_prefix_chars: new_arg_strings.append(arg_string) # replace arguments referencing files with the file content else: try: args_file = open(arg_string[1:]) try: arg_strings = [] for arg_line in args_file.read().splitlines(): for arg in self.convert_arg_line_to_args(arg_line): arg_strings.append(arg) arg_strings = self._read_args_from_files(arg_strings) new_arg_strings.extend(arg_strings) finally: args_file.close() except IOError: err = _sys.exc_info()[1] self.error(str(err)) # return the modified argument list return new_arg_strings def convert_arg_line_to_args(self, arg_line): return [arg_line] def _match_argument(self, action, arg_strings_pattern): # match the pattern for this action to the arg strings nargs_pattern = self._get_nargs_pattern(action) match = _re.match(nargs_pattern, arg_strings_pattern) # raise an exception if we weren't able to find a match if match is None: nargs_errors = { None: _('expected one argument'), OPTIONAL: _('expected at most one argument'), ONE_OR_MORE: _('expected at least one argument'), } default = _('expected %s argument(s)') % action.nargs msg = nargs_errors.get(action.nargs, default) raise ArgumentError(action, msg) # return the number of arguments matched return len(match.group(1)) def _match_arguments_partial(self, actions, arg_strings_pattern): # progressively shorten the actions list by slicing off the # final actions until we find a match result = [] for i in range(len(actions), 0, -1): actions_slice = actions[:i] pattern = ''.join([self._get_nargs_pattern(action) for action in actions_slice]) match = _re.match(pattern, arg_strings_pattern) if match is not None: result.extend([len(string) for string in match.groups()]) break # return the list of arg string counts return result def _parse_optional(self, arg_string): # if it's an empty string, it was meant to be a positional if not arg_string: return None # if it doesn't start with a prefix, it was meant to be positional if not arg_string[0] in self.prefix_chars: return None # if the option string is present in the parser, return the action if arg_string in self._option_string_actions: action = self._option_string_actions[arg_string] return action, arg_string, None # if it's just a single character, it was meant to be positional if len(arg_string) == 1: return None # if the option string before the "=" is present, return the action if '=' in arg_string: option_string, explicit_arg = arg_string.split('=', 1) if option_string in self._option_string_actions: action = self._option_string_actions[option_string] return action, option_string, explicit_arg # search through all possible prefixes of the option string # and all actions in the parser for possible interpretations option_tuples = self._get_option_tuples(arg_string) # if multiple actions match, the option string was ambiguous if len(option_tuples) > 1: options = ', '.join([option_string for action, option_string, explicit_arg in option_tuples]) tup = arg_string, options self.error(_('ambiguous option: %s could match %s') % tup) # if exactly one action matched, this segmentation is good, # so return the parsed action elif len(option_tuples) == 1: option_tuple, = option_tuples return option_tuple # if it was not found as an option, but it looks like a negative # number, it was meant to be positional # unless there are negative-number-like options if self._negative_number_matcher.match(arg_string): if not self._has_negative_number_optionals: return None # if it contains a space, it was meant to be a positional if ' ' in arg_string: return None # it was meant to be an optional but there is no such option # in this parser (though it might be a valid option in a subparser) return None, arg_string, None def _get_option_tuples(self, option_string): result = [] # option strings starting with two prefix characters are only # split at the '=' chars = self.prefix_chars if option_string[0] in chars and option_string[1] in chars: if '=' in option_string: option_prefix, explicit_arg = option_string.split('=', 1) else: option_prefix = option_string explicit_arg = None for option_string in self._option_string_actions: if option_string.startswith(option_prefix): action = self._option_string_actions[option_string] tup = action, option_string, explicit_arg result.append(tup) # single character options can be concatenated with their arguments # but multiple character options always have to have their argument # separate elif option_string[0] in chars and option_string[1] not in chars: option_prefix = option_string explicit_arg = None short_option_prefix = option_string[:2] short_explicit_arg = option_string[2:] for option_string in self._option_string_actions: if option_string == short_option_prefix: action = self._option_string_actions[option_string] tup = action, option_string, short_explicit_arg result.append(tup) elif option_string.startswith(option_prefix): action = self._option_string_actions[option_string] tup = action, option_string, explicit_arg result.append(tup) # shouldn't ever get here else: self.error(_('unexpected option string: %s') % option_string) # return the collected option tuples return result def _get_nargs_pattern(self, action): # in all examples below, we have to allow for '--' args # which are represented as '-' in the pattern nargs = action.nargs # the default (None) is assumed to be a single argument if nargs is None: nargs_pattern = '(-*A-*)' # allow zero or one arguments elif nargs == OPTIONAL: nargs_pattern = '(-*A?-*)' # allow zero or more arguments elif nargs == ZERO_OR_MORE: nargs_pattern = '(-*[A-]*)' # allow one or more arguments elif nargs == ONE_OR_MORE: nargs_pattern = '(-*A[A-]*)' # allow any number of options or arguments elif nargs == REMAINDER: nargs_pattern = '([-AO]*)' # allow one argument followed by any number of options or arguments elif nargs == PARSER: nargs_pattern = '(-*A[-AO]*)' # all others should be integers else: nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) # if this is an optional action, -- is not allowed if action.option_strings: nargs_pattern = nargs_pattern.replace('-*', '') nargs_pattern = nargs_pattern.replace('-', '') # return the pattern return nargs_pattern # ======================== # Value conversion methods # ======================== def _get_values(self, action, arg_strings): # for everything but PARSER args, strip out '--' if action.nargs not in [PARSER, REMAINDER]: arg_strings = [s for s in arg_strings if s != '--'] # optional argument produces a default when not present if not arg_strings and action.nargs == OPTIONAL: if action.option_strings: value = action.const else: value = action.default if isinstance(value, basestring): value = self._get_value(action, value) self._check_value(action, value) # when nargs='*' on a positional, if there were no command-line # args, use the default if it is anything other than None elif (not arg_strings and action.nargs == ZERO_OR_MORE and not action.option_strings): if action.default is not None: value = action.default else: value = arg_strings self._check_value(action, value) # single argument or optional argument produces a single value elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: arg_string, = arg_strings value = self._get_value(action, arg_string) self._check_value(action, value) # REMAINDER arguments convert all values, checking none elif action.nargs == REMAINDER: value = [self._get_value(action, v) for v in arg_strings] # PARSER arguments convert all values, but check only the first elif action.nargs == PARSER: value = [self._get_value(action, v) for v in arg_strings] self._check_value(action, value[0]) # all other types of nargs produce a list else: value = [self._get_value(action, v) for v in arg_strings] for v in value: self._check_value(action, v) # return the converted value return value def _get_value(self, action, arg_string): type_func = self._registry_get('type', action.type, action.type) if not _callable(type_func): msg = _('%r is not callable') raise ArgumentError(action, msg % type_func) # convert the value to the appropriate type try: result = type_func(arg_string) # ArgumentTypeErrors indicate errors except ArgumentTypeError: name = getattr(action.type, '__name__', repr(action.type)) msg = str(_sys.exc_info()[1]) raise ArgumentError(action, msg) # TypeErrors or ValueErrors also indicate errors except (TypeError, ValueError): name = getattr(action.type, '__name__', repr(action.type)) msg = _('invalid %s value: %r') raise ArgumentError(action, msg % (name, arg_string)) # return the converted value return result def _check_value(self, action, value): # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: tup = value, ', '.join(map(repr, action.choices)) msg = _('invalid choice: %r (choose from %s)') % tup raise ArgumentError(action, msg) # ======================= # Help-formatting methods # ======================= def format_usage(self): formatter = self._get_formatter() formatter.add_usage(self.usage, self._actions, self._mutually_exclusive_groups) return formatter.format_help() def format_help(self): formatter = self._get_formatter() # usage formatter.add_usage(self.usage, self._actions, self._mutually_exclusive_groups) # description formatter.add_text(self.description) # positionals, optionals and user-defined groups for action_group in self._action_groups: formatter.start_section(action_group.title) formatter.add_text(action_group.description) formatter.add_arguments(action_group._group_actions) formatter.end_section() # epilog formatter.add_text(self.epilog) # determine help from format above return formatter.format_help() def format_version(self): import warnings warnings.warn( 'The format_version method is deprecated -- the "version" ' 'argument to ArgumentParser is no longer supported.', DeprecationWarning) formatter = self._get_formatter() formatter.add_text(self.version) return formatter.format_help() def _get_formatter(self): return self.formatter_class(prog=self.prog) # ===================== # Help-printing methods # ===================== def print_usage(self, file=None): if file is None: file = _sys.stdout self._print_message(self.format_usage(), file) def print_help(self, file=None): if file is None: file = _sys.stdout self._print_message(self.format_help(), file) def print_version(self, file=None): import warnings warnings.warn( 'The print_version method is deprecated -- the "version" ' 'argument to ArgumentParser is no longer supported.', DeprecationWarning) self._print_message(self.format_version(), file) def _print_message(self, message, file=None): if message: if file is None: file = _sys.stderr file.write(message) # =============== # Exiting methods # =============== def exit(self, status=0, message=None): if message: self._print_message(message, _sys.stderr) _sys.exit(status) def error(self, message): """error(message: string) Prints a usage message incorporating the message to stderr and exits. If you override this in a subclass, it should not return -- it should either exit or raise an exception. """ self.print_usage(_sys.stderr) self.exit(2, _('%s: error: %s\n') % (self.prog, message)) cppcheck-1.66/tools/aws.py000066400000000000000000000027751236713773000155560ustar00rootroot00000000000000#!/usr/bin/python # amazon web services script import subprocess import os import urllib def wget(url): try: fp = urllib.urlopen(url) data = fp.read() return data except IOError: pass return '' # Perform a git pull. def gitpull(): try: subprocess.call(['git', 'pull']) except IOError: pass except OSError: pass return False def daca2(foldernum): folders = '0123456789abcdefghijklmnopqrstuvwxyz' folder = folders[foldernum % len(folders)] if (foldernum / len(folders)) % 2 == 1: folder = 'lib' + folder print('Daca2 folder=' + folder) p = subprocess.Popen(['git', 'show', '--format=%h'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() rev = comm[0] rev = rev[:rev.find('\n')] gitpull() subprocess.call( ['nice', 'make', 'SRCDIR=build', 'CXXFLAGS=-O2', 'CPPFLAGS=-DMAXTIME=600']) subprocess.call( ['mv', 'cppcheck', os.path.expanduser('~/daca2/cppcheck-O2')]) subprocess.call( ['cp', 'cfg/std.cfg', os.path.expanduser('~/daca2/')]) subprocess.call(['python', 'tools/daca2.py', folder, '--rev=' + rev]) subprocess.call(['cp', os.path.expanduser('~/daca2/' + folder + '/results.txt'), os.path.expanduser('~/daca2/results-' + folder + '.txt')]) subprocess.call(['make', 'clean']) foldernum = 0 while True: daca2(foldernum) foldernum = foldernum + 1 cppcheck-1.66/tools/ci.py000066400000000000000000000111051236713773000153420ustar00rootroot00000000000000#!/usr/bin/python # continuous integration # build daily reports (doxygen,coverage,etc) import datetime import time import subprocess import pexpect import glob import os import sys import urllib def wget(url): try: fp = urllib.urlopen(url) data = fp.read() return data except IOError: pass return '' # 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: pass except OSError: pass except pexpect.TIMEOUT: pass # Perform a 'make test' on the repo def maketest(preclean): if preclean == True: subprocess.call(['make', 'clean']) p = subprocess.Popen( ['nice', 'make', 'test'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() f = open('maketest.txt', 'wt') f.write('Errors\n======\n') f.write(comm[1] + '\n') f.write('Output\n======\n') f.write(comm[0] + '\n') f.close() upload('maketest.txt', 'htdocs/devinfo/') # 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: pass except OSError: pass except pexpect.TIMEOUT: pass def iconv(filename): p = subprocess.Popen(['file', '-i', filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) comm = p.communicate() if comm[0].find('charset=iso-8859-1') >= 0: subprocess.call( ["iconv", filename, "--from=ISO-8859-1", "--to=UTF-8", "-o", filename]) # Generate daily webreport def generate_webreport(): filenames = glob.glob('*/*.cpp') for filename in filenames: 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: pass except OSError: pass except pexpect.TIMEOUT: pass except pexpect.EOF: return True return False def daca2report(): print('Generate DACA2 report') subprocess.call(['rm', '-rf', 'daca2-report']) subprocess.call(['mkdir', 'daca2-report']) subprocess.call(['python', 'tools/daca2-report.py', 'daca2-report']) upload('-r daca2-report', 'htdocs/devinfo/') def daca2(foldernum): folders = '0123456789abcdefghijklmnopqrstuvwxyz' folder = folders[foldernum % len(folders)] print('Daca2 folder=' + folder) p = subprocess.Popen(['git', 'show', '--format=%h'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() rev = comm[0] rev = rev[:rev.find('\n')] subprocess.call( ['make', 'clean']) subprocess.call( ['nice', 'make', 'SRCDIR=build', 'CFGDIR=' + os.path.expanduser('~/cppcheck/cfg'), 'CXXFLAGS=-O2', 'CPPFLAGS=-DMAXTIME=600']) subprocess.call( ['mv', 'cppcheck', os.path.expanduser('~/daca2/cppcheck-O2')]) subprocess.call(['python', 'tools/daca2.py', folder, '--rev=' + rev]) daca2report() subprocess.call( ['python', 'tools/daca2.py', 'lib' + folder, '--rev=' + rev]) daca2report() t0 = datetime.date.today() foldernum = 0 while True: if datetime.date.today() != t0: print("generate daily reports") t0 = datetime.date.today() generate_webreport() if gitpull() == True: print("make test") # maketest(False) # Integral make test build cmd = wget('http://cppcheck.sourceforge.net/cgi-bin/ci.cgi?clear') if cmd.find("doxygen") >= 0: generate_webreport() daca2(foldernum) foldernum = foldernum + 1 cppcheck-1.66/tools/cppcheckdata.py000066400000000000000000000234141236713773000173670ustar00rootroot00000000000000# Python module that loads a cppcheck dump # License: No restrictions, use this as you need. from lxml import etree class Token: Id = None str = None next = None previous = None scopeId = None scope = None isName = None isNumber = None isInt = None isFloat = None isString = None strlen = None isChar = None isOp = None isArithmeticalOp = None isAssignmentOp = None isComparisonOp = None isLogicalOp = None linkId = None link = None varId = None variableId = None variable = None functionId = None function = None valuesId = None values = 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 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 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.astParent = IdMap[self.astParentId] self.astOperand1 = IdMap[self.astOperand1Id] self.astOperand2 = IdMap[self.astOperand2Id] # Get value if it exists # Returns None if it doesn't exist def getValue(self,v): if not self.values: return None for value in self.values: if value.intvalue == v: return value return None class Scope: Id = None classStartId = None classStart = None classEndId = None classEnd = None className = 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('nestedId') 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: Id = None argument = None argumentId = None def __init__(self,element): self.Id = element.get('id') 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.iteritems(): self.argument[argnr] = IdMap[argid] class Variable: Id = None nameTokenId = None nameToken = None typeStartTokenId = None typeStartToken = None typeEndTokenId = None typeEndToken = None isArgument = None isArray = None isClass = None isLocal = None isPointer = None isReference = None isStatic = None 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') self.isArray = element.get('isArray') self.isClass = element.get('isClass') self.isLocal = element.get('isLocal') self.isPointer = element.get('isPointer') self.isReference = element.get('isReference') self.isStatic = element.get('isStatic') def setId(self, IdMap): self.nameToken = IdMap[self.nameTokenId] self.typeStartToken = IdMap[self.typeStartTokenId] self.typeEndToken = IdMap[self.typeEndTokenId] class ValueFlow: class Value: intvalue = None condition = None def __init__(self, element): self.intvalue = int(element.get('intvalue')) self.condition = element.get('condition-line') if self.condition: self.condition = int(self.condition) Id = None values = None def __init__(self, element): self.Id = element.get('id') self.values = [] for value in element: self.values.append(ValueFlow.Value(value)) class CppcheckData: tokenlist = [] scopes = [] functions = [] variables = [] valueflow = [] def __init__(self, filename): self.tokenlist = [] self.scopes = [] self.variables = [] self.valueflow = [] data = etree.parse(filename) for element in data.getroot(): 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 = {} IdMap[None] = None IdMap['0'] = 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) def parsedump(filename): return CppcheckData(filename) # Check if type of ast node is float/double def astIsFloat(token): if not token: return False if token.str == '.': return astIsFloat(token.astOperand2) if '+-*/%'.find(token.str) == 0: if True == astIsFloat(token.astOperand1): return True return 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 cppcheck-1.66/tools/daca2-download.py000066400000000000000000000112241236713773000175300ustar00rootroot00000000000000#!/usr/bin/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 subprocess import sys import shutil import glob import os import datetime import time DEBIAN = ['ftp://ftp.sunet.se/pub/Linux/distributions/Debian/debian/', 'http://ftp.sunet.se/pub/Linux/distributions/Debian/debian/', 'ftp://ftp.debian.org/debian/'] def wget(filepath): filename = filepath if filepath.find('/') >= 0: 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 line.find('.orig.tar.') > 0: 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) else: raise def removeAll(): count = 5 while count > 0: count = count - 1 filenames = [] for g in glob.glob('[#_A-Za-z0-9]*'): filenames.append(g) for g in glob.glob('.[A-Za-z]*'): filenames.append(g) try: for filename in filenames: if os.path.isdir(filename): shutil.rmtree(filename, onerror=handleRemoveReadonly) else: os.remove(filename) except WindowsError, err: time.sleep(30) if count == 0: print('Failed to cleanup files/folders') print(err) sys.exit(1) continue except OSError, 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:] != '.C' and g[-2:] != '.c' and g[-4:] != '.cc' and g[-4:] != '.cpp' and g[-4:] != '.cxx' and g[-2:] != '.h' and g[-2:] != '.H' and g[-4:] != '.c++' and g[-4:] != '.hpp' and g[-4:] != '.tpp' and g[-4:] != '.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.66/tools/daca2-report.py000066400000000000000000000107521236713773000172410ustar00rootroot00000000000000import os import sys import subprocess def readdate(data): datepos = -1 if data[:5] == 'DATE ': datepos = 0 else: datepos = data.find('\nDATE ') if datepos >= 0: datepos = datepos + 1 if datepos < 0: return None datestr = '' datepos = datepos + 5 while True: if datepos >= len(data): return None d = data[datepos] if d >= '0' and d <= '9': datestr = datestr + d elif d == '\n': if len(datestr) == 8: return datestr[:4] + '-' + datestr[4:6] + '-' + datestr[6:] return None elif d != ' ' and d != '-': return None datepos = datepos + 1 path = '.' if len(sys.argv) == 2: path = sys.argv[1] 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 100kb 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 = os.path.expanduser('~/daca2/') for lib in range(2): for a in "0123456789abcdefghijklmnopqrstuvwxyz": if lib == 1: a = "lib" + a if os.path.isfile(daca2 + a + '/results.txt'): f = open(daca2 + a + '/results.txt', 'rt') data = f.read() f.close() datestr = readdate(data) if os.path.isfile(daca2 + 'results-' + a + '.txt'): f2 = open(daca2 + 'results-' + a + '.txt') data2 = f2.read() f2.close() datestr2 = readdate(data2) if not datestr or datestr < datestr2: data = data2 datestr = datestr2 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.66/tools/daca2.py000066400000000000000000000142041236713773000157240ustar00rootroot00000000000000#!/usr/bin/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 subprocess import sys import shutil import glob import os import datetime import time DEBIAN = ['ftp://ftp.sunet.se/pub/Linux/distributions/Debian/debian/', 'http://ftp.sunet.se/pub/Linux/distributions/Debian/debian/', 'ftp://ftp.debian.org/debian/'] def wget(filepath): filename = filepath if filepath.find('/') >= 0: 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 line.find('.orig.tar.') > 0: 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) else: raise def removeAllExceptResults(): count = 5 while count > 0: count = count - 1 filenames = [] for g in glob.glob('[A-Za-z0-9]*'): filenames.append(g) for g in glob.glob('.[a-z]*'): filenames.append(g) try: for filename in filenames: if os.path.isdir(filename): shutil.rmtree(filename, onerror=handleRemoveReadonly) elif filename != 'results.txt': os.remove(filename) except WindowsError, err: time.sleep(30) if count == 0: print('Failed to cleanup files/folders') print(err) sys.exit(1) continue except OSError, 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 path.find('/clang/INPUTS/') > 0 or statinfo.st_size > 100000: os.remove(g) def scanarchive(filepath): # 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]) if filename[:5] == 'flite' or filename[:5] == 'boost' or filename[:6] == 'iceowl' or filename[:7] == 'insight': 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', '-D__GCC__', '--enable=style', '--error-exitcode=0', '--suppressions-list=../suppressions.txt', '.'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() results = open('results.txt', 'at') if p.returncode == 0: results.write(comm[1]) elif comm[0].find('cppcheck: error: could not find or open any of the paths given.') < 0: results.write('Exit code is not zero! Crash?\n') results.write('\n') results.close() FOLDER = None REV = None for arg in sys.argv[1:]: if arg[:6] == '--rev=': REV = arg[6:] 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/suppressions.txt') if not os.path.isfile(workdir + 'suppressions.txt'): suppressions = open(workdir + 'suppressions.txt', 'wt') suppressions.write('\n') suppressions.close() 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) 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.66/tools/dmake.cpp000066400000000000000000000420611236713773000161670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General 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" static std::string builddir(std::string filename); static std::string objfile(std::string cppfile); static void getDeps(const std::string &filename, std::vector &depfiles); std::string builddir(std::string filename) { if (filename.compare(0,4,"lib/") == 0) filename = "$(SRCDIR)" + filename.substr(3); return filename; } std::string objfile(std::string cppfile) { cppfile.erase(cppfile.rfind(".")); return builddir(cppfile + ".o"); } 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) { 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) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o " << objfile(files[i]) << " " << builddir(files[i]) << "\n\n"; } } static void getCppFiles(std::vector &files, const std::string &path) { std::map filemap; FileLister::recursiveAddFiles(filemap, path); // 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"; } static std::string getLibName(const std::string &path) { // path can be e.g. "externals/foo/foo.cpp" then returned // library name is "FOO". std::string libName = path.substr(path.find('/')+1); libName = libName.substr(0, libName.find('/')); std::transform(libName.begin(), libName.end(),libName.begin(), ::toupper); return libName; } static void makeExtObj(std::ostream &fout, const std::vector &externalfiles) { bool start = true; std::ostringstream libNames; std::string libName; for (unsigned int i = 0; i < externalfiles.size(); ++i) { if (start) { libName = getLibName(externalfiles[i]); fout << "ifndef " << libName << "\n"; fout << " " << libName << " = " << objfile(externalfiles[i]); libNames << "EXTOBJ += $(" << libName << ")\n"; start = false; } else { fout << std::string(14, ' ') << objfile(externalfiles[i]); } if (i+1 >= externalfiles.size() || libName != getLibName(externalfiles[i+1])) { // This was the last file for this library fout << "\nendif\n\n\n"; start = true; } else { // There are more files for this library fout << " \\\n"; } } fout << libNames.str(); } int main(int argc, char **argv) { const bool release(argc >= 2 && std::string(argv[1]) == "--release"); // Get files.. std::vector libfiles; getCppFiles(libfiles, "lib/"); std::vector clifiles; getCppFiles(clifiles, "cli/"); std::vector testfiles; getCppFiles(testfiles, "test/"); std::vector toolsfiles; getCppFiles(toolsfiles, "tools/"); if (libfiles.empty() && clifiles.empty() && testfiles.empty()) { std::cerr << "No files found. Are you in the correct directory?" << std::endl; return EXIT_FAILURE; } std::vector externalfiles; getCppFiles(externalfiles, "externals/"); // 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 << "BASEPATH = ../externals/tinyxml/\n"; fout1 << "include($$PWD/../externals/tinyxml/tinyxml.pri)\n"; fout1 << "BASEPATH = ../lib/\n"; fout1 << "INCLUDEPATH += ../externals/tinyxml\n"; fout1 << "HEADERS += $${BASEPATH}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, ' ') << "$${BASEPATH}" << fname << ".h"; if (i + 1 < testfiles.size()) fout1 << " \\\n"; } fout1 << "\n\nSOURCES += "; for (unsigned int i = 0; i < libfiles.size(); ++i) { fout1 << "$${BASEPATH}" << 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)\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"; // Makefile settings.. if (release) { makeConditionalVariable(fout, "CXXFLAGS", "-O2 -include lib/cxx11emu.h -DNDEBUG -Wall"); } 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 " "-Wfloat-equal " "-Winline " // "-Wlogical-op " "-Wmissing-declarations " "-Wmissing-format-attribute " "-Wno-long-long " "-Woverloaded-virtual " "-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 "$(CPPCHK_GLIBCXX_DEBUG) " "-g"); } fout << "ifeq ($(HAVE_RULES),yes)\n" << " 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, "CXX", "g++"); makeConditionalVariable(fout, "PREFIX", "/usr"); makeConditionalVariable(fout, "INCLUDE_FOR_LIB", "-Ilib -Iexternals/tinyxml"); makeConditionalVariable(fout, "INCLUDE_FOR_CLI", "-Ilib -Iexternals/tinyxml"); makeConditionalVariable(fout, "INCLUDE_FOR_TEST", "-Ilib -Icli -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 << "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"; makeExtObj(fout, externalfiles); fout << ".PHONY: dmake\n\n"; fout << "\n###### Targets\n\n"; fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++0x -o cppcheck $(CLIOBJ) $(LIBOBJ) $(EXTOBJ) $(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 cli/pathmatch.o\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++0x -o testrunner $(TESTOBJ) $(LIBOBJ) cli/threadexecutor.o cli/cppcheckexecutor.o cli/cmdlineparser.o cli/filelister.o cli/pathmatch.o $(EXTOBJ) $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "test:\tall\n"; fout << "\t./testrunner\n\n"; fout << "check:\tall\n"; fout << "\t./testrunner -g -q\n\n"; fout << "dmake:\ttools/dmake.o cli/filelister.o lib/path.o\n"; fout << "\t$(CXX) $(CXXFLAGS) -std=c++0x -o dmake tools/dmake.o cli/filelister.o lib/path.o -Ilib $(LDFLAGS)\n"; fout << "\t./dmake\n\n"; fout << "reduce:\ttools/reduce.o externals/tinyxml/tinyxml2.o $(LIBOBJ)\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++0x -g -o reduce tools/reduce.o -Ilib -Iexternals/tinyxml $(LIBOBJ) $(LIBS) externals/tinyxml/tinyxml2.o $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "clean:\n"; fout << "\trm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/tinyxml/*.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 .\n\n"; fout << "install: cppcheck\n"; fout << "\tinstall -d ${BIN}\n"; fout << "\tinstall cppcheck ${BIN}\n"; fout << "\tinstall htmlreport/cppcheck-htmlreport ${BIN}\n"; fout << "ifdef CFGDIR \n"; fout << "\tinstall -d ${CFGDIR}\n"; fout << "\tinstall -m 644 cfg/* ${CFGDIR}\n"; fout << "endif\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, externalfiles, "${INCLUDE_FOR_LIB}"); compilefiles(fout, toolsfiles, "${INCLUDE_FOR_LIB}"); return 0; } cppcheck-1.66/tools/dmake.vcproj000066400000000000000000000110221236713773000167010ustar00rootroot00000000000000 cppcheck-1.66/tools/extracttests.py000077500000000000000000000235461236713773000175230ustar00rootroot00000000000000#!/usr/bin/python # # Cppcheck - A tool for static C/C++ code analysis # Copyright (C) 2007-2014 Daniel Marjamaeki and Cppcheck team. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General 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 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.. 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 len(code) > 10: node = {'testclass': testclass, 'functionName': functionName, 'code': code, 'expected': res.group(1)} self.nodes.append(node) code = '' # 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] path/testfile.cpp') sys.exit(0) # parse command line xml = False filename = None htmldir = None codedir = None for arg in sys.argv[1:]: if arg == '--xml': xml = 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 not functionname 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: 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.66/tools/matchcompiler.py000077500000000000000000000532771236713773000176210ustar00rootroot00000000000000#!/usr/bin/python # # Cppcheck - A tool for static C/C++ code analysis # Copyright (C) 2007-2014 Daniel Marjamaeki and Cppcheck team. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General 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 os import sys import re import glob import argparse class MatchCompiler: def __init__(self, verify_mode=False): self._verifyMode = verify_mode self._reset() def _reset(self): self._rawMatchFunctions = [] self._matchStrs = {} self._matchFunctionCache = {} def _generateCacheSignature( self, 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 def _insertMatchStr(self, look_for): prefix = 'matchStr' # Add entry if needed if look_for not in self._matchStrs: pos = len(self._matchStrs) + 1 self._matchStrs[look_for] = pos return prefix + str(self._matchStrs[look_for]) def _compileCmd(self, tok): if tok == '%any%': return 'true' elif tok == '%bool%': return 'tok->isBoolean()' elif tok == '%char%': return '(tok->type()==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->type() == Token::eBitOp && tok->str()==' + self._insertMatchStr('|') + ')/* | */' elif tok == '%oror%': return '(tok->type() == Token::eLogicalOp && tok->str()==' + self._insertMatchStr('||') + ')/* || */' elif tok == '%str%': return '(tok->type()==Token::eString)' elif tok == '%type%': return ( '(tok->isName() && tok->varId()==0U && !tok->isKeyword())' ) elif tok == '%var%': return 'tok->isName()' elif tok == '%varid%': return '(tok->isName() && tok->varId()==varid)' elif (len(tok) > 2) and (tok[0] == "%"): print ("unhandled:" + tok) return ( '(tok->str()==' + self._insertMatchStr(tok) + ')/* ' + tok + ' */' ) def _compilePattern(self, pattern, nr, varid, isFindMatch=False, tokenType="const Token"): ret = '' returnStatement = '' 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 tok.find('%varid%') != -1 and checked_varid is False: 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 = None neg = None if "" in tokens2: ret += ' if (tok && (' logicalOp = ' || ' neg = '' else: ret += ' if (!tok || !(' logicalOp = ' || ' neg = '' first = True for tok2 in tokens2: if tok2 == '': continue if not first: ret += logicalOp first = False ret += neg + self._compileCmd(tok2) if "" in tokens2: ret += '))\n' ret += ' tok = tok->next();\n' gotoNextToken = '' else: ret += '))\n' ret += ' ' + returnStatement # !!a elif tok[0:2] == "!!": ret += ' if (tok && tok->str() == ' + self._insertMatchStr( tok[2:]) + ')/* ' + tok[2:] + ' */\n' ret += ' ' + returnStatement gotoNextToken = ' tok = tok ? tok->next() : NULL;\n' else: ret += ' if (!tok || !' + self._compileCmd(tok) + ')\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 def parseMatch(self, 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 = [] ret.append(line[pos1:pos + 1]) for arg in args: ret.append(arg) 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 def _parseStringComparison(self, line, pos1): startPos = 0 endPos = 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 def _compileVerifyTokenMatch( self, 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): 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: break # Non-const pattern - bailout pattern = res.group(1) line = self._replaceSpecificTokenMatch( is_simplematch, line, pos1, end_pos, pattern, tok, varId) return line def _compileVerifyTokenFindMatch( self, 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): pos1 = 0 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(len(res) >= 3 or len(res) < 6) # assert that Token::find(simple)match has either 2, 3 or # four arguments g0 = res[0] tok = res[1] pattern = res[2] # Check for varId varId = None if not is_findsimplematch and g0.find("%varid%") != -1: 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 is True and len(res) == 4: endToken = res[3] elif is_findsimplematch is False: if varId and len(res) == 5: endToken = res[3] elif varId is None and len(res) == 4: endToken = res[3] res = re.match(r'\s*"([^"]*)"\s*$', pattern) if res is None: 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('str\(\) (==|!=) "', line) if not match: match = re.search('strAt\(.+?\) (==|!=) "', line) if not match: 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] + self._insertMatchStr(text) + line[endPos:] return line def convertFile(self, srcname, destname): self._reset() fin = open(srcname, "rt") srclines = fin.readlines() fin.close() header = '#include "token.h"\n' header += '#include "errorlogger.h"\n' header += '#include \n' header += '#include \n' # header += '#include \n' code = '' for line in srclines: # Compile Token::Match and Token::simpleMatch line = self._replaceTokenMatch(line) # Compile Token::findsimplematch line = self._replaceTokenFindMatch(line) # Cache plain C-strings in C++ strings line = self._replaceCStrings(line) code += line # Compute string list stringList = '' for match in sorted(self._matchStrs, key=self._matchStrs.get): stringList += 'static const std::string matchStr' + \ str(self._matchStrs[match]) + '("' + match + '");\n' # Compute matchFunctions strFunctions = '' for function in self._rawMatchFunctions: strFunctions += function fout = open(destname, 'wt') fout.write(header + stringList + strFunctions + code) fout.close() def main(): # Main program build_dir = 'build' # Check if we are invoked from the right place if not os.path.exists('lib') and not os.path.exists('samples'): print('Please invoke from the top level cppcheck source dir. Example: tools/matchcompiler.py') sys.exit(-1) # Create build directory if needed if not os.path.exists(build_dir): os.makedirs(build_dir) if not os.path.isdir(build_dir): raise Exception(build_dir + ' is not a directory') # 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!') args = parser.parse_args() mc = MatchCompiler(verify_mode=args.verify) # convert all lib/*.cpp files for f in glob.glob('lib/*.cpp'): print (f + ' => ' + build_dir + '/' + f[4:]) mc.convertFile(f, build_dir + '/' + f[4:]) if __name__ == '__main__': main() cppcheck-1.66/tools/readme.md000066400000000000000000000043341236713773000161620ustar00rootroot00000000000000## 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.66/tools/reduce.cpp000066400000000000000000000767571236713773000164000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public 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: 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=] 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.66/tools/test_matchcompiler.py000077500000000000000000000165471236713773000206570ustar00rootroot00000000000000#!/usr/bin/python # # Cppcheck - A tool for static C/C++ code analysis # Copyright (C) 2007-2014 Daniel Marjamaeki and Cppcheck team. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General 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) self.assertEqual(output, 'if (match1(tok)) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) input = 'if (Token::Match(tok->next()->next(), "foobar %type% %num%")) {' output = self.mc._replaceTokenMatch(input) self.assertEqual(output, 'if (match2(tok->next()->next())) {') self.assertEqual(1, len(self.mc._matchStrs)) input = 'if (Token::Match(tok, "foo\"special\"bar %num%")) {' output = self.mc._replaceTokenMatch(input) # FIXME: Currently detected as non-static pattern self.assertEqual( output, 'if (Token::Match(tok, "foo"special"bar %num%")) {') # self.assertEqual(3, len(self.mc._matchStrs)) def test_replaceTokenMatchWithVarId(self): input = 'if (Token::Match(tok, "foobar %varid%", 123)) {' output = self.mc._replaceTokenMatch(input) self.assertEqual(output, 'if (match1(tok, 123)) {') self.assertEqual(1, len(self.mc._matchStrs)) input = 'if (Token::Match(tok->next()->next(), "%varid% foobar", tok->varId())) {' output = self.mc._replaceTokenMatch(input) self.assertEqual( output, 'if (match2(tok->next()->next(), tok->varId())) {') self.assertEqual(1, len(self.mc._matchStrs)) input = 'if (Token::Match(tok, "foo\"special\"bar %type% %varid%", my_varid_cache)) {' output = self.mc._replaceTokenMatch(input) # FIXME: Currently detected as non-static pattern self.assertEqual( output, 'if (Token::Match(tok, "foo"special"bar %type% %varid%", my_varid_cache)) {') # self.assertEqual(1, len(self.mc._matchStrs)) # test caching: reuse existing matchX() input = 'if (Token::Match(tok, "foobar %varid%", 123)) {' output = self.mc._replaceTokenMatch(input) self.assertEqual(output, 'if (match1(tok, 123)) {') self.assertEqual(1, len(self.mc._matchStrs)) # two in one line input = 'if (Token::Match(tok, "foobar2 %varid%", 123) || Token::Match(tok, "%type% %varid%", 123)) {' output = self.mc._replaceTokenMatch(input) self.assertEqual(output, 'if (match3(tok, 123) || match4(tok, 123)) {') self.assertEqual(2, len(self.mc._matchStrs)) def test_replaceTokenSimpleMatch(self): input = 'if (Token::simpleMatch(tok, "foobar")) {' output = self.mc._replaceTokenMatch(input) self.assertEqual(output, 'if (match1(tok)) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) input = 'if (Token::simpleMatch(tok->next()->next(), "foobar")) {' output = self.mc._replaceTokenMatch(input) self.assertEqual(output, 'if (match1(tok->next()->next())) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) input = 'if (Token::simpleMatch(tok, "foo\"special\"bar")) {' output = self.mc._replaceTokenMatch(input) # FIXME: Currently detected as non-static pattern self.assertEqual( output, 'if (Token::simpleMatch(tok, "foo\"special\"bar")) {') self.assertEqual(1, len(self.mc._matchStrs)) def test_replaceTokenFindSimpleMatch(self): input = 'if (Token::findsimplematch(tok, "foobar")) {' output = self.mc._replaceTokenFindMatch(input) self.assertEqual(output, 'if (findmatch1(tok)) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) input = 'if (Token::findsimplematch(tok->next()->next(), "foobar", tok->link())) {' output = self.mc._replaceTokenFindMatch(input) self.assertEqual( output, 'if (findmatch2(tok->next()->next(), tok->link())) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) input = 'if (Token::findsimplematch(tok, "foo\"special\"bar")) {' output = self.mc._replaceTokenFindMatch(input) # FIXME: Currently detected as non-static pattern self.assertEqual( output, 'if (Token::findsimplematch(tok, "foo\"special\"bar")) {') self.assertEqual(1, len(self.mc._matchStrs)) def test_replaceTokenFindMatch(self): input = 'if (Token::findmatch(tok, "foobar")) {' output = self.mc._replaceTokenFindMatch(input) self.assertEqual(output, 'if (findmatch1(tok)) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) # findmatch with varid input = 'if (Token::findmatch(tok, "foobar %varid%", tok->varId())) {' output = self.mc._replaceTokenFindMatch(input) self.assertEqual(output, 'if (findmatch2(tok, tok->varId())) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) # findmatch with end token input = 'if (Token::findmatch(tok->next()->next(), "foobar %type%", tok->link())) {' output = self.mc._replaceTokenFindMatch(input) self.assertEqual( output, 'if (findmatch3(tok->next()->next(), tok->link())) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) # findmatch with end token and varid input = 'if (Token::findmatch(tok->next()->next(), "foobar %type% %varid%", tok->link(), 123)) {' output = self.mc._replaceTokenFindMatch(input) self.assertEqual( output, 'if (findmatch4(tok->next()->next(), tok->link(), 123)) {') self.assertEqual(1, len(self.mc._matchStrs)) self.assertEqual(1, self.mc._matchStrs['foobar']) if __name__ == '__main__': unittest.main() cppcheck-1.66/tools/test_showtimetop5.sh000077500000000000000000000001631236713773000204440ustar00rootroot00000000000000#!/bin/bash if [[ "`./cppcheck --showtime=top5 cli/cmdlineparser.h --quiet | wc -l`" != 7 ]] ; then false fi cppcheck-1.66/tools/times-tags.sh000077500000000000000000000011661236713773000170170ustar00rootroot00000000000000#!/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.66/tools/times.c000066400000000000000000000022171236713773000156660ustar00rootroot00000000000000#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.66/tools/times.sh000077500000000000000000000021441236713773000160600ustar00rootroot00000000000000#!/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! # 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 4`; do ./cppcheck --quiet --showtime=summary --enable=all --inconclusive src 2> /dev/null | tee -a times.log done grep "Overall" times.log | tail -4 | sed s/s// | awk -v "i=$i" -v "git_head=$git_head" '{ sum+=$3} END {print "Run " i", "git_head " Average: " sum/4}' | 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.66/tools/tools.pro000066400000000000000000000004641236713773000162650ustar00rootroot00000000000000TEMPLATE = app TARGET = dmake DEPENDPATH += . INCLUDEPATH += . ../lib OBJECTS_DIR = temp CONFIG += warn_on CONFIG -= qt app_bundle include(../console_common.pri) SOURCES += dmake.cpp \ ../cli/filelister.cpp \ ../lib/path.cpp HEADERS += ../cli/filelister.h \ ../lib/path.h cppcheck-1.66/webreport.sh000077500000000000000000000003711236713773000156100ustar00rootroot00000000000000#!/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 # Detect duplicate code.. ~/pmd-4.2.6/bin/cpd.sh lib/ > devinfo/cpd.txt cppcheck-1.66/win_installer/000077500000000000000000000000001236713773000161115ustar00rootroot00000000000000cppcheck-1.66/win_installer/GPLv3.rtf000066400000000000000000001213571236713773000175320ustar00rootroot00000000000000{\rtf1\ansi\deff0\adeflang1025 {\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset0 Times New Roman;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\fnil\fprq2\fcharset0 Arial Unicode MS;}{\f4\fnil\fprq2\fcharset0 MS Mincho;}{\f5\fnil\fprq2\fcharset0 Tahoma;}{\f6\fnil\fprq0\fcharset0 Tahoma;}} {\colortbl;\red0\green0\blue0;\red128\green128\blue128;} {\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\snext1 Normal;} {\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\af4\langfe255\hich\f2\fs28\lang1035\loch\f2\fs28\lang1035\sbasedon1\snext3 Heading;} {\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\sbasedon1\snext3 Body Text;} {\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\sbasedon3\snext4 List;} {\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ai\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\i\loch\f0\fs24\lang1035\i\sbasedon1\snext5 caption;} {\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\sbasedon1\snext6 Index;} } {\info{\author Kimmo Varis}{\creatim\yr2010\mo1\dy17\hr1\min15}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern3100}}\deftab709 {\*\pgdsctbl {\pgdsc0\pgdscuse195\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}} \paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 GNU GENERAL PUBLIC LICENSE\line Version 3, 29 June 2007\line \line Copyright (C) 2007 Free Software Foundation, Inc. \line Everyone is permitted to copy and distribute verbatim copies\line of this license document, but changing it is not allowed.\line \line Preamble\line \line The GNU General Public License is a free, copyleft license for\line software and other kinds of works.\line \line The licenses for most software and other practical works are designed\line to take away yo ur freedom to share and change the works. By contrast,\line the GNU General Public License is intended to guarantee your freedom to\line share and change all versions of a program--to make sure it remains free\line software for all its users. We, the Free Software Foun dation, use the\line GNU General Public License for most of our software; it applies also to\line any other work released this way by its authors. You can apply it to\line your programs, too.\line \line When we speak of free software, we are referring to freedom, not\line price. Ou r General Public Licenses are designed to make sure that you\line have the freedom to distribute copies of free software (and charge for\line them if you wish), that you receive source code or can get it if you\line want it, that you can change the software or use pieces of it in new\line free programs, and that you know you can do these things.\line \line To protect your rights, we need to prevent others from denying you\line these rights or asking you to surrender the rights. Therefore, you have\line certain responsibilities if you distribut e copies of the software, or if\line you modify it: responsibilities to respect the freedom of others.\line \line For example, if you distribute copies of such a program, whether\line gratis or for a fee, you must pass on to the recipients the same\line freedoms that you receive d. You must make sure that they, too, receive\line or can get the source code. And you must show them these terms so they\line know their rights.\line \line Developers that use the GNU GPL protect your rights with two steps:\line (1) assert copyright on the software, and (2) o ffer you this License\line giving you legal permission to copy, distribute and/or modify it.\line \line For the developers' and authors' protection, the GPL clearly explains\line that there is no warranty for this free software. For both users' and\line authors' sake, the GPL r equires that modified versions be marked as\line changed, so that their problems will not be attributed erroneously to\line authors of previous versions.\line \line Some devices are designed to deny users access to install or run\line modified versions of the software inside the m, although the manufacturer\line can do so. This is fundamentally incompatible with the aim of\line protecting users' freedom to change the software. The systematic\line pattern of such abuse occurs in the area of products for individuals to\line use, which is precisely wh ere it is most unacceptable. Therefore, we\line have designed this version of the GPL to prohibit the practice for those\line products. If such problems arise substantially in other domains, we\line stand ready to extend this provision to those domains in future versio ns\line of the GPL, as needed to protect the freedom of users.\line \line Finally, every program is threatened constantly by software patents.\line States should not allow patents to restrict development and use of\line software on general-purpose computers, but in those that do , we wish to\line avoid the special danger that patents applied to a free program could\line make it effectively proprietary. To prevent this, the GPL assures that\line patents cannot be used to render the program non-free.\line \line The precise terms and conditions for copyin g, distribution and\line modification follow.\line \line TERMS AND CONDITIONS\line \line 0. Definitions.\line \line "This License" refers to version 3 of the GNU General Public License.\line \line "Copyright" also means copyright-like laws that apply to other kinds of\line wor ks, such as semiconductor masks.\line \line "The Program" refers to any copyrightable work licensed under this\line License. Each licensee is addressed as "you". "Licensees" and\line "recipients" may be individuals or organizations.\line \line To "modify" a work means to copy fro m or adapt all or part of the work\line in a fashion requiring copyright permission, other than the making of an\line exact copy. The resulting work is called a "modified version" of the\line earlier work or a work "based on" the earlier work.\line \line A "covered work" means either the unmodified Program or a work based\line on the Program.\line \line To "propagate" a work means to do anything with it that, without\line permission, would make you directly or secondarily liable for\line infringement under applicable copyright law, except executing it on a\line computer or modifying a private copy. Propagation includes copying,\line distribution (with or without modification), making available to the\line public, and in some countries other activities as well.\line \line To "convey" a work means any kind of propagation that enables other\line parties to make or receive copies. Mere interaction with a user through\line a computer network, with no transfer of a copy, is not conveying.\line \line An interactive user interface displays "Appropriate Legal Notices"\line to the extent that it includes a convenient and prominently visible\line feature that (1) displays an appropriate copyright notice, and (2)\line tells the user that there is no warranty for the work (except to the\line extent that warranties are provided), that licensees may convey the\line work under this License, and how to view a copy of this License. If\line the interface presents a list of user commands or options, such as a\line menu, a prominent item in the list meets this criterion.\line \line 1. Source Code.\line \line The "source code" for a work means the preferred form o f the work\line for making modifications to it. "Object code" means any non-source\line form of a work.\line \line A "Standard Interface" means an interface that either is an official\line standard defined by a recognized standards body, or, in the case of\line interfaces specified for a particular programming language, one that\line is widely used among developers working in that language.\line \line The "System Libraries" of an executable work include anything, other\line than the work as a whole, that (a) is included in the normal form of\line packaging a Major Component, but which is not part of that Major\line Component, and (b) serves only to enable use of the work with that\line Major Component, or to implement a Standard Interface for which an\line implementation is available to the public in source code form. A\line "Major Component", in this context, means a major essential component\line (kernel, window system, and so on) of the specific operating system\line (if any) on which the executable work runs, or a compiler used to\line produce the work, or an object code interpreter used to run it.\line \line The "Corresponding Source" for a work in object code form means all\line the source code needed to generate, install, and (for an executable\line work) run the object code and to modify the work, including scripts to\line control those activities. However , it does not include the work's\line System Libraries, or general-purpose tools or generally available free\line programs which are used unmodified in performing those activities but\line which are not part of the work. For example, Corresponding Source\line includes interf ace definition files associated with source files for\line the work, and the source code for shared libraries and dynamically\line linked subprograms that the work is specifically designed to require,\line such as by intimate data communication or control flow between th ose\line subprograms and other parts of the work.\line \line The Corresponding Source need not include anything that users\line can regenerate automatically from other parts of the Corresponding\line Source.\line \line The Corresponding Source for a work in source code form is that\line same work.\line \line 2. Basic Permissions.\line \line All rights granted under this License are granted for the term of\line copyright on the Program, and are irrevocable provided the stated\line conditions are met. This License explicitly affirms your unlimited\line permission to run the unmodified Program. The output from running a\line covered work is covered by this License only if the output, given its\line content, constitutes a covered work. This License acknowledges your\line rights of fair use or other equivalent, as provided by copyright law. \line \line You may make, run and propagate covered works that you do not\line convey, without conditions so long as your license otherwise remains\line in force. You may convey covered works to others for the sole purpose\line of having them make modifications exclusively for you, or provide you\line with facilities for running those works, provided that you comply with\line the terms of this License in conveying all material for which you do\line not control copyright. Those thus making or running the covered works\line for you must do so exclus ively on your behalf, under your direction\line and control, on terms that prohibit them from making any copies of\line your copyrighted material outside their relationship with you.\line \line Conveying under any other circumstances is permitted solely under\line the conditions stated below. Sublicensing is not allowed; section 10\line makes it unnecessary.\line \line 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\line \line No covered work shall be deemed part of an effective technological\line measure under any applicable law fulfillin g obligations under article\line 11 of the WIPO copyright treaty adopted on 20 December 1996, or\line similar laws prohibiting or restricting circumvention of such\line measures.\line \line When you convey a covered work, you waive any legal power to forbid\line circumvention of tech nological measures to the extent such circumvention\line is effected by exercising rights under this License with respect to\line the covered work, and you disclaim any intention to limit operation or\line modification of the work as a means of enforcing, against the wor k's\line users, your or third parties' legal rights to forbid circumvention of\line technological measures.\line \line 4. Conveying Verbatim Copies.\line \line You may convey verbatim copies of the Program's} \par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 source code as you\line receive it, in any medium, provided that you conspicuously and\line appropriately publish on each copy an appropriate copyright notice;\line keep intact all notices stating that this License and any\line non-permissive terms added in accord with secti on 7 apply to the code;\line keep intact all notices of the absence of any warranty; and give all\line recipients a copy of this License along with the Program.\line \line You may charge any price or no price for each copy that you convey,\line and you may offer support or warra nty protection for a fee.\line \line 5. Conveying Modified Source Versions.\line \line You may convey a work based on the Program, or the modifications to\line produce it from the Program, in the form of source code under the\line terms of section 4, provided that you also meet all of these conditions:\line \line a) The work must carry prominent notices stating that you modified\line it, and giving a relevant date.\line \line b) The work must carry prominent notices stating that it is\line released under this License and any conditions added unde r section\line 7. This requirement modifies the requirement in section 4 to\line "keep intact all notices".\line \line c) You must license the entire work, as a whole, under this\line License to anyone who comes into possession of a copy. This\line License will th erefore apply, along with any applicable section 7\line additional terms, to the whole of the work, and all its parts,\line regardless of how they are packaged. This License gives no\line permission to license the work in any other way, but it does not\line i nvalidate such permission if you have separately received it.\line \line d) If the work has interactive user interfaces, each must display\line Appropriate Legal Notices; however, if the Program has interactive\line interfaces that do not display Appropriate Legal Notices, your\line work need not make them do so.\line \line A compilation of a covered work with other separate and independent\line works, which are not by their nature extensions of the covered work,\line and which are not combined with it such as to form a larger progra m,\line in or on a volume of a storage or distribution medium, is called an\line "aggregate" if the compilation and its resulting copyright are not\line used to limit the access or legal rights of the compilation's users\line beyond what the individual works permit. Inclusio n of a covered work\line in an aggregate does not cause this License to apply to the other\line parts of the aggregate.\line \line 6. Conveying Non-Source Forms.\line \line You may convey a covered work in object code form under the terms\line of sections 4 and 5, provided that you also convey the\line machine-readable Corresponding Source under the terms of this License,\line in one of these ways:\line \line a) Convey the object code in, or embodied in, a physical product\line (including a physical distribution medium), accompanied by the\line Correspond ing Source fixed on a durable physical medium\line customarily used for software interchange.\line \line b) Convey the object code in, or embodied in, a physical product\line (including a physical distribution medium), accompanied by a\line written offer, valid for at least three years and valid for as\line long as you offer spare parts or customer support for that product\line model, to give anyone who possesses the object code either (1) a\line copy of the Corresponding Source for all the software in the\line product that is covered by this License, on a durable physical\line medium customarily used for software interchange, for a price no\line more than your reasonable cost of physically performing this\line conveying of source, or (2) access to copy the\line Correspondin g Source from a network server at no charge.\line \line c) Convey individual copies of the object code with a copy of the\line written offer to provide the Corresponding Source. This\line alternative is allowed only occasionally and noncommercially, and\line only if you received the object code with such an offer, in accord\line with subsection 6b.\line \line d) Convey the object code by offering access from a designated\line place (gratis or for a charge), and offer equivalent access to the\line Corresponding Source in the same way through the same place at no\line further charge. You need not require recipients to copy the\line Corresponding Source along with the object code. If the place to\line copy the object code is a network server, the Corresponding Source\line may be on a different server (operated by you or a third party)\line that supports equivalent copying facilities, provided you maintain\line clear directions next to the object code saying where to find the\line Corresponding Source. Regardless of what server hosts the\line Corresponding Source, you remain obligated to ensure that it is\line available for as long as needed to satisfy these requirements.\line \line e) Convey the object code using peer-to-peer transmission, provided\line you inform other peers where the object code and Corresponding\line Source of the work are being offered to the general public at no\line charge under subsection 6d.\line \line A separable portion of the object code, whose source code is excluded\line from the Corresponding Source as a System Library, need no t be\line included in conveying the object code work.\line \line A "User Product" is either (1) a "consumer product", which means any\line tangible personal property which is normally used for personal, family,\line or household purposes, or (2) anything designed or sold for inc orporation\line into a dwelling. In determining whether a product is a consumer product,\line doubtful cases shall be resolved in favor of coverage. For a particular\line product received by a particular user, "normally used" refers to a\line typical or common use of that c lass of product, regardless of the status\line of the particular user or of the way in which the particular user\line actually uses, or expects or is expected to use, the product. A product\line is a consumer product regardless of whether the product has substantial\line com mercial, industrial or non-consumer uses, unless such uses represent\line the only significant mode of use of the product.\line \line "Installation Information" for a User Product means any methods,\line procedures, authorization keys, or other information required to insta ll\line and execute modified versions of a covered work in that User Product from\line a modified version of its Corresponding Source. The information must\line suffice to ensure that the continued functioning of the modified object\line code is in no case prevented or inter fered with solely because\line modification has been made.\line \line If you convey an object code work under this section in, or with, or\line specifically for use in, a User Product, and the conveying occurs as\line part of a transaction in which the right of possession and us e of the\line User Product is transferred to the recipient in perpetuity or for a\line fixed term (regardless of how the transaction is characterized), the\line Corresponding Source conveyed under this section must be accompanied\line by the Installation Information. But thi s requirement does not apply\line if neither you nor any third party retains the ability to install\line modified object code on the User Product (for example, the work has\line been installed in ROM).\line \line The requirement to provide Installation Information does not inclu de a\line requirement to continue to provide support service, warranty, or updates\line for a work that has been modified or installed by the recipient, or for\line the User Product in which it has been modified or installed. Access to a\line network may be denied when the m odification itself materially and\line adversely affects the operation of the network or violates the rules and\line protocols for communication across the network.\line \line Corresponding Source conveyed, and Installation Information provided,\line in accord with this section must be in a format that is publicly\line documented (and with an implementation available to the public in\line source code form), and must require no special password or key for\line unpacking, reading or copying.\line \line 7. Additional Terms.\line \line "Additional permissions" are terms that supplement the terms of this\line License by making exceptions from one or more of its conditions.\line Additional permissions that are applicable to the entire Program shall\line be treated as though they were included in this License, to the extent\line that the y are valid under applicable law. If additional permissions\line apply only to part of the Program, that part may be used separately\line under those permissions, but the entire Program remains governed by\line this License without regard to the additional permissions.\line \line When you convey a copy of a covered work, you may at your option\line remove any additional permissions from that copy, or from any part of\line it. (Additional permissions may be written to require their own\line removal in certain cases when you modify the work.) You may place\line additional permissions on material, added by you to a covered work,\line for which you have or can give appropriate copyright permission.\line \line Notwithstanding any other provision of this License, for material you\line add to a covered work, you may (if a uthorized by the copyright holders of\line that material) supplement the terms of this License with terms:\line \line a) Disclaiming warranty or limiting liability differently from the\line terms of sections 15 and 16 of this License; or\line \line b) Requiring preservation of specified reasonable legal notices or\line author attributions in that material or in the Appropriate Legal\line Notices displayed by works containing it; or\line \line c) Prohibiting misrepresentation of the origin of that material, or\line requiring that modi fied versions of such material be marked in\line reasonable ways as different from the original version; or\line \line d) Limiting the use for publicity purposes of names of licensors or\line authors of the material; or\line \line e) Declining to grant rights under trad emark law for use of some\line trade names, trademarks, or service marks; or\line \line f) Requiring indemnification of licensors and authors of that\line material by anyone who conveys} \par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 the material (or modified versions of\line it) with contractual assumptions of liability to the recipient, for\line any liability that these contractual assumptions directly impose on\line those licensors and authors.\line \line All other non-permissive additional t erms are considered "further\line restrictions" within the meaning of section 10. If the Program as you\line received it, or any part of it, contains a notice stating that it is\line governed by this License along with a term that is a further\line restriction, you may remov e that term. If a license document contains\line a further restriction but permits relicensing or conveying under this\line License, you may add to a covered work material governed by the terms\line of that license document, provided that the further restriction does\line no t survive such relicensing or conveying.\line \line If you add terms to a covered work in accord with this section, you\line must place, in the relevant source files, a statement of the\line additional terms that apply to those files, or a notice indicating\line where to find th e applicable terms.\line \line Additional terms, permissive or non-permissive, may be stated in the\line form of a separately written license, or stated as exceptions;\line the above requirements apply either way.\line \line 8. Termination.\line \line You may not propagate or modify a cove red work except as expressly\line provided under this License. Any attempt otherwise to propagate or\line modify it is void, and will automatically terminate your rights under\line this License (including any patent licenses granted under the third\line paragraph of section 11).\line \line However, if you cease all violation of this License, then your\line license from a particular copyright holder is reinstated (a)\line provisionally, unless and until the copyright holder explicitly and\line finally terminates your license, and (b) permanently, if the copyright\line holder fails to notify you of the violation by some reasonable means\line prior to 60 days after the cessation.\line \line Moreover, your license from a particular copyright holder is\line reinstated permanently if the copyright holder notifies you of the\line vio lation by some reasonable means, this is the first time you have\line received notice of violation of this License (for any work) from that\line copyright holder, and you cure the violation prior to 30 days after\line your receipt of the notice.\line \line Termination of your ri ghts under this section does not terminate the\line licenses of parties who have received copies or rights from you under\line this License. If your rights have been terminated and not permanently\line reinstated, you do not qualify to receive new licenses for the same\line material under section 10.\line \line 9. Acceptance Not Required for Having Copies.\line \line You are not required to accept this License in order to receive or\line run a copy of the Program. Ancillary propagation of a covered work\line occurring solely as a consequence of using peer-to-peer transmission\line to receive a copy likewise does not require acceptance. However,\line nothing other than this License grants you permission to propagate or\line modify any covered work. These actions infringe copyright if you do\line not accept this License. Therefore, by modifying or propagating a\line covered work, you indicate your acceptance of this License to do so.\line \line 10. Automatic Licensing of Downstream Recipients.\line \line Each time you convey a covered work, the recipient automatically\line receives a license from the original licensors, to run, modify and\line propagate that work, subject to this License. You are not responsible\line for enforcing compliance by third parties with this License.\line \line An "entity transaction" is a transaction transferring control of an\line organizat ion, or substantially all assets of one, or subdividing an\line organization, or merging organizations. If propagation of a covered\line work results from an entity transaction, each party to that\line transaction who receives a copy of the work also receives whatever\line l icenses to the work the party's predecessor in interest had or could\line give under the previous paragraph, plus a right to possession of the\line Corresponding Source of the work from the predecessor in interest, if\line the predecessor has it or can get it with reason able efforts.\line \line You may not impose any further restrictions on the exercise of the\line rights granted or affirmed under this License. For example, you may\line not impose a license fee, royalty, or other charge for exercise of\line rights granted under this License, a nd you may not initiate litigation\line (including a cross-claim or counterclaim in a lawsuit) alleging that\line any patent claim is infringed by making, using, selling, offering for\line sale, or importing the Program or any portion of it.\line \line 11. Patents.\line \line A "contrib utor" is a copyright holder who authorizes use under this\line License of the Program or a work on which the Program is based. The\line work thus licensed is called the contributor's "contributor version".\line \line A contributor's "essential patent claims" are all patent claims\line owned or controlled by the contributor, whether already acquired or\line hereafter acquired, that would be infringed by some manner, permitted\line by this License, of making, using, or selling its contributor version,\line but do not include claims that would be infringed only as a\line consequence of further modification of the contributor version. For\line purposes of this definition, "control" includes the right to grant\line patent sublicenses in a manner consistent with the requirements of\line this License.\line \line Each contributo r grants you a non-exclusive, worldwide, royalty-free\line patent license under the contributor's essential patent claims, to\line make, use, sell, offer for sale, import and otherwise run, modify and\line propagate the contents of its contributor version.\line \line In the foll owing three paragraphs, a "patent license" is any express\line agreement or commitment, however denominated, not to enforce a patent\line (such as an express permission to practice a patent or covenant not to\line sue for patent infringement). To "grant" such a patent l icense to a\line party means to make such an agreement or commitment not to enforce a\line patent against the party.\line \line If you convey a covered work, knowingly relying on a patent license,\line and the Corresponding Source of the work is not available for anyone\line to copy, free of charge and under the terms of this License, through a\line publicly available network server or other readily accessible means,\line then you must either (1) cause the Corresponding Source to be so\line available, or (2) arrange to deprive yourself of the benefi t of the\line patent license for this particular work, or (3) arrange, in a manner\line consistent with the requirements of this License, to extend the patent\line license to downstream recipients. "Knowingly relying" means you have\line actual knowledge that, but for the pa tent license, your conveying the\line covered work in a country, or your recipient's use of the covered work\line in a country, would infringe one or more identifiable patents in that\line country that you have reason to believe are valid.\line \line If, pursuant to or in connec tion with a single transaction or\line arrangement, you convey, or propagate by procuring conveyance of, a\line covered work, and grant a patent license to some of the parties\line receiving the covered work authorizing them to use, propagate, modify\line or convey a specific copy of the covered work, then the patent license\line you grant is automatically extended to all recipients of the covered\line work and works based on it.\line \line A patent license is "discriminatory" if it does not include within\line the scope of its coverage, prohibits t he exercise of, or is\line conditioned on the non-exercise of one or more of the rights that are\line specifically granted under this License. You may not convey a covered\line work if you are a party to an arrangement with a third party that is\line in the business of distr ibuting software, under which you make payment\line to the third party based on the extent of your activity of conveying\line the work, and under which the third party grants, to any of the\line parties who would receive the covered work from you, a discriminatory\line patent license (a) in connection with copies of the covered work\line conveyed by you (or copies made from those copies), or (b) primarily\line for and in connection with specific products or compilations that\line contain the covered work, unless you entered into that arrange ment,\line or that patent license was granted, prior to 28 March 2007.\line \line Nothing in this License shall be construed as excluding or limiting\line any implied license or other defenses to infringement that may\line otherwise be available to you under applicable patent la w.\line \line 12. No Surrender of Others' Freedom.\line \line If conditions are imposed on you (whether by court order, agreement or\line otherwise) that contradict the conditions of this License, they do not\line excuse you from the conditions of this License. If you cannot conve y a\line covered work so as to satisfy simultaneously your obligations under this\line License and any other pertinent obligations, then as a consequence you may\line not convey it at all. For example, if you agree to terms that obligate you\line to collect a royalty for fur ther conveying from those to whom you convey\line the Program, the only way you could satisfy both those terms and this\line License would be to refrain entirely from conveying the Program.\line \line 13. Use with the GNU Affero General Public License.\line \line Notwithstanding an y other provision of this License, you have\line permission to link or combine any covered work with a work licensed\line under version 3 of the GNU Affero General Public License into a single\line combined work, and to convey the resulting work. The terms of this\line Licen se will continue to apply to the part which is the covered work,\line but the special requirements of the GNU Affero General Public License,\line section 13, concerning interaction through a network will apply to the\line combination as such.\line \line 14. Revised Versions of t his License.\line \line The Free Software Foundation may publish revised and/or new versions of\line the GNU General Public License from time to time. Such new versions will\line be similar} \par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 in spirit to the present version, but may differ in detail to\line address new problems or concerns.\line \line Each version is given a distinguishing version number. If the\line Program specifies that a certain numbered version of the GNU General\line Public License "or any l ater version" applies to it, you have the\line option of following the terms and conditions either of that numbered\line version or of any later version published by the Free Software\line Foundation. If the Program does not specify a version number of the\line GNU General P ublic License, you may choose any version ever published\line by the Free Software Foundation.\line \line If the Program specifies that a proxy can decide which future\line versions of the GNU General Public License can be used, that proxy's\line public statement of acceptance o f a version permanently authorizes you\line to choose that version for the Program.\line \line Later license versions may give you additional or different\line permissions. However, no additional obligations are imposed on any\line author or copyright holder as a result of your choosing to follow a\line later version.\line \line 15. Disclaimer of Warranty.\line \line THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\line APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\line HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY\line OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\line THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\line PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\line IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\line ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\line \line 16. Limitation of Liability.\line \line IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\line WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PAR TY WHO MODIFIES AND/OR CONVEYS\line THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\line GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\line USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\line DA TA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\line PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\line EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\line SUCH DAMAGES.\line \line 17. Interpretation of Sections 15 and 16.\line \line If the disclaimer of warranty and limitation of liability provided\line above cannot be given local legal effect according to their terms,\line reviewing courts shall apply local law that most closely approximates\line an absolute waiver of all civil liability in connection with the\line Program, unless a warranty or assumption of liability accompanies a\line copy of the Program in return for a fee.\line \line END OF TERMS AND CONDITIONS\line \line How to Apply These Terms to Your New Programs \line \line If you develop a new program, and you want it to be of the greatest\line possible use to the public, the best way to achieve this is to make it\line free software which everyone can redistribute and change under these terms.\line \line To do so, attach the following not ices to the program. It is safest\line to attach them to the start of each source file to most effectively\line state the exclusion of warranty; and each file should have at least\line the "copyright" line and a pointer to where the full notice is found.\line \line \line Copyright (C) \line \line This program is free software: you can redistribute it and/or modify\line it under the terms of the GNU General Public License as published by\line the Free Software Foundation, either version 3 of the License, or\line (at your option) any later version.\line \line This program is distributed in the hope that it will be useful,\line but WITHOUT ANY WARRANTY; without even the implied warranty of\line MERCHANTAB ILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\line GNU General Public License for more details.\line \line You should have received a copy of the GNU General Public License\line along with this program. If not, see .\line \line Also add information on how to contact you by electronic and paper mail.\line \line If the program does terminal interaction, make it output a short\line notice like this when it starts in an interactive mode:\line \line Copyright (C) \line This prog ram comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\line This is free software, and you are welcome to redistribute it\line under certain conditions; type `show c' for details.\line \line The hypothetical commands `show w' and `show c' should show the ap propriate\line parts of the General Public License. Of course, your program's commands\line might be different; for a GUI interface, you would use an "about box".\line \line You should also get your employer (if you work as a programmer) or school,\line if any, to sign a "copyr ight disclaimer" for the program, if necessary.\line For more information on this, and how to apply and follow the GNU GPL, see\line .\line \line The GNU General Public License does not permit incorporating your program\line into proprietary program s. If your program is a subroutine library, you\line may consider it more useful to permit linking proprietary applications with\line the library. If this is what you want to do, use the GNU Lesser General\line Public License instead of this License. But first, please read\line .\line } \par }cppcheck-1.66/win_installer/config.wxi000066400000000000000000000014401236713773000201060ustar00rootroot00000000000000 cppcheck-1.66/win_installer/cppcheck.wixproj000066400000000000000000000027001236713773000213140ustar00rootroot00000000000000 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.66/win_installer/cppcheck.wxs000066400000000000000000000272431236713773000204440ustar00rootroot00000000000000 NOT Installed NOT Installed NOT Installed NOT Installed NOT Installed cppcheck-1.66/win_installer/images/000077500000000000000000000000001236713773000173565ustar00rootroot00000000000000cppcheck-1.66/win_installer/images/banner.jpg000066400000000000000000000047451236713773000213370ustar00rootroot00000000000000JFIF__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.66/win_installer/readme.txt000066400000000000000000000024021236713773000201050ustar00rootroot00000000000000The Wix Installer for Windows ============================= cppcheck Windows installer is created with WiX: http://wixtoolset.org/ You'll need: - WiX - MSBuild (coming with Visual Studio (also with Express edition)) - VS 2010 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: QtCore4.dll and QtGui4.dll - MS CRT merge module (Microsoft_VC100_CRT_x86.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.